| Index: chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
|
| diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..81001e2e0aa4fa7ce191046b3f77351fa0c14042
|
| --- /dev/null
|
| +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
|
| @@ -0,0 +1,1523 @@
|
| +// 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.contextualsearch;
|
| +
|
| +import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
|
| +import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_PHONE;
|
| +import static org.chromium.content.browser.test.util.CriteriaHelper.DEFAULT_POLLING_INTERVAL;
|
| +
|
| +import android.content.Context;
|
| +import android.content.pm.ActivityInfo;
|
| +import android.graphics.Point;
|
| +import android.os.SystemClock;
|
| +import android.test.FlakyTest;
|
| +import android.test.suitebuilder.annotation.SmallTest;
|
| +import android.text.TextUtils;
|
| +import android.view.KeyEvent;
|
| +
|
| +import com.google.android.apps.chrome.R;
|
| +
|
| +import org.chromium.base.ThreadUtils;
|
| +import org.chromium.base.test.util.CommandLineFlags;
|
| +import org.chromium.base.test.util.Feature;
|
| +import org.chromium.base.test.util.Restriction;
|
| +import org.chromium.chrome.browser.ChromeActivity;
|
| +import org.chromium.chrome.browser.ChromeSwitches;
|
| +import org.chromium.chrome.browser.ChromeTabbedActivity;
|
| +import org.chromium.chrome.browser.CompositorChromeActivity;
|
| +import org.chromium.chrome.browser.Tab;
|
| +import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanel.PanelState;
|
| +import org.chromium.chrome.browser.compositor.bottombar.contextualsearch.ContextualSearchPanelDelegate;
|
| +import org.chromium.chrome.browser.omnibox.UrlBar;
|
| +import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
|
| +import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
|
| +import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
|
| +import org.chromium.chrome.browser.tabmodel.TabModelUtils;
|
| +import org.chromium.chrome.browser.util.FeatureUtilities;
|
| +import org.chromium.chrome.test.ChromeActivityTestCaseBase;
|
| +import org.chromium.chrome.test.util.ChromeTabUtils;
|
| +import org.chromium.chrome.test.util.OmniboxTestUtils;
|
| +import org.chromium.chrome.test.util.TestHttpServerClient;
|
| +import org.chromium.content.browser.ContentViewCore;
|
| +import org.chromium.content.browser.test.util.CallbackHelper;
|
| +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 java.util.concurrent.TimeoutException;
|
| +
|
| +/**
|
| + * Tests the Contextual Search Manager using instrumentation tests.
|
| + */
|
| +@CommandLineFlags.Add({
|
| + ChromeSwitches.ENABLE_CONTEXTUAL_SEARCH_FOR_TESTING
|
| + })
|
| +public class ContextualSearchManagerTest extends ChromeActivityTestCaseBase<ChromeActivity> {
|
| +
|
| + private static final String TEST_PAGE =
|
| + TestHttpServerClient.getUrl("chrome/test/data/android/contextualsearch/tap_test.html");
|
| + private static final int TEST_TIMEOUT = 15000;
|
| +
|
| + // TODO(donnd): get these from TemplateURL once the low-priority or Contextual Search API
|
| + // is fully supported.
|
| + private static final String NORMAL_PRIORITY_SEARCH_ENDPOINT = "/search?";
|
| + private static final String LOW_PRIORITY_SEARCH_ENDPOINT = "/s?";
|
| + private static final String CONTEXTUAL_SEARCH_PREFETCH_PARAM = "&pf=c";
|
| +
|
| + private ContextualSearchManager mManager;
|
| + private ContextualSearchFakeServer mFakeServer;
|
| + private ContextualSearchPanelDelegate mPanelDelegate;
|
| + private ContextualSearchSelectionController mSelectionController;
|
| + private ContextualSearchPolicy mPolicy;
|
| + private ChromePreferenceManager mPreferenceManager;
|
| +
|
| + public ContextualSearchManagerTest() {
|
| + super(ChromeActivity.class);
|
| + }
|
| +
|
| + @Override
|
| + protected void setUp() throws Exception {
|
| + super.setUp();
|
| +
|
| + ChromeActivity activity = getActivity();
|
| + if (activity instanceof CompositorChromeActivity) {
|
| + mManager = ((CompositorChromeActivity) activity).getContextualSearchManager();
|
| + }
|
| +
|
| + if (mManager != null) {
|
| + mFakeServer = new ContextualSearchFakeServer(mManager);
|
| + mManager.setNetworkCommunicator(mFakeServer);
|
| + mPanelDelegate = mManager.getContextualSearchPanelDelegate();
|
| + mSelectionController = mManager.getSelectionController();
|
| + mPolicy = ContextualSearchPolicy.getInstance(getActivity());
|
| + mPreferenceManager = ChromePreferenceManager.getInstance(getActivity());
|
| +
|
| + mPolicy.overrideDecidedStateForTesting(true);
|
| + resetTapCounters();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void startMainActivity() throws InterruptedException {
|
| + startMainActivityWithURL(TEST_PAGE);
|
| + }
|
| +
|
| + /**
|
| + * Simulates a click on the given node.
|
| + * @param nodeId A string containing the node ID.
|
| + */
|
| + private void clickNode(String nodeId) throws InterruptedException, TimeoutException {
|
| + Tab tab = getActivity().getActivityTab();
|
| + DOMUtils.clickNode(this, tab.getContentViewCore(), nodeId);
|
| + }
|
| +
|
| + /**
|
| + * Simulates a click on the given word node.
|
| + * Waits for the bar to peek.
|
| + * @param nodeId A string containing the node ID.
|
| + */
|
| + private void clickWordNode(String nodeId) throws InterruptedException, TimeoutException {
|
| + clickNode(nodeId);
|
| + waitForPanelToPeekAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Simulates a key press.
|
| + * @param keycode The key's code.
|
| + */
|
| + private void pressKey(int keycode) {
|
| + getInstrumentation().sendKeyDownUpSync(keycode);
|
| + getInstrumentation().waitForIdleSync();
|
| + }
|
| +
|
| + /**
|
| + * Simulates pressing back button.
|
| + */
|
| + private void pressBackButton() {
|
| + pressKey(KeyEvent.KEYCODE_BACK);
|
| + }
|
| +
|
| + /**
|
| + * @return The selected text.
|
| + */
|
| + private String getSelectedText() {
|
| + return mSelectionController.getSelectedText();
|
| + }
|
| +
|
| + /**
|
| + * Simulates a long-press on the given node.
|
| + * @param nodeId A string containing the node ID.
|
| + */
|
| + private void longPressNode(String nodeId) throws InterruptedException, TimeoutException {
|
| + Tab tab = getActivity().getActivityTab();
|
| + DOMUtils.longPressNode(this, tab.getContentViewCore(), nodeId);
|
| + waitForPanelToPeekAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Posts a fake response on the Main thread.
|
| + */
|
| + private final class FakeResponseOnMainThread implements Runnable {
|
| +
|
| + private final boolean mIsNetworkUnavailable;
|
| + private final int mResponseCode;
|
| + private final String mSearchTerm;
|
| + private final String mDisplayText;
|
| + private final String mAlternateTerm;
|
| + private final boolean mDoPreventPreload;
|
| +
|
| + public FakeResponseOnMainThread(boolean isNetworkUnavailable, int responseCode,
|
| + String searchTerm, String displayText, String alternateTerm,
|
| + boolean doPreventPreload) {
|
| + mIsNetworkUnavailable = isNetworkUnavailable;
|
| + mResponseCode = responseCode;
|
| + mSearchTerm = searchTerm;
|
| + mDisplayText = displayText;
|
| + mAlternateTerm = alternateTerm;
|
| + mDoPreventPreload = doPreventPreload;
|
| + }
|
| +
|
| + @Override
|
| + public void run() {
|
| + mFakeServer.handleSearchTermResolutionResponse(
|
| + mIsNetworkUnavailable, mResponseCode, mSearchTerm, mDisplayText,
|
| + mAlternateTerm, mDoPreventPreload);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Fakes a server response with the parameters given.
|
| + * {@See ContextualSearchManager#handleSearchTermResolutionResponse}.
|
| + */
|
| + private void fakeResponse(boolean isNetworkUnavailable, int responseCode,
|
| + String searchTerm, String displayText, String alternateTerm, boolean doPreventPreload) {
|
| + if (mFakeServer.getSearchTermRequested() != null) {
|
| + getInstrumentation().runOnMainSync(
|
| + new FakeResponseOnMainThread(isNetworkUnavailable, responseCode, searchTerm,
|
| + displayText, alternateTerm, doPreventPreload));
|
| + }
|
| + }
|
| +
|
| + private void assertContainsParameters(String searchTerm, String alternateTerm) {
|
| + assertTrue(mFakeServer == null || mFakeServer.getSearchTermRequested() == null
|
| + || mFakeServer.getLoadedUrl().contains(searchTerm)
|
| + && mFakeServer.getLoadedUrl().contains(alternateTerm));
|
| + }
|
| +
|
| + private void assertContainsNoParameters() {
|
| + assertTrue(mFakeServer == null || mFakeServer.getLoadedUrl() == null);
|
| + }
|
| +
|
| + private void assertSearchTermRequested() {
|
| + assertNotNull(mFakeServer.getSearchTermRequested());
|
| + }
|
| +
|
| + private void assertSearchTermNotRequested() {
|
| + assertNull(mFakeServer.getSearchTermRequested());
|
| + }
|
| +
|
| + private void assertPanelClosedOrUndefined() {
|
| + boolean success = false;
|
| + if (mPanelDelegate == null) {
|
| + success = true;
|
| + } else {
|
| + PanelState panelState = mPanelDelegate.getPanelState();
|
| + success = panelState == PanelState.CLOSED || panelState == PanelState.UNDEFINED;
|
| + }
|
| + assertTrue(success);
|
| + }
|
| +
|
| + private void assertLoadedNoUrl() {
|
| + assertTrue("Requested a search or preload when none was expected!",
|
| + (mFakeServer == null || mFakeServer.getLoadedUrl() == null));
|
| + }
|
| +
|
| + private void assertLoadedLowPriorityUrl() {
|
| + if (mFakeServer == null) return;
|
| + String message = "Expected a low priority search request URL, but got "
|
| + + (mFakeServer.getLoadedUrl() != null ? mFakeServer.getLoadedUrl() : "null");
|
| + assertTrue(message, mFakeServer.getLoadedUrl() != null
|
| + && mFakeServer.getLoadedUrl().contains(LOW_PRIORITY_SEARCH_ENDPOINT));
|
| + assertTrue("Low priority request does not have the required prefetch parameter!",
|
| + mFakeServer.getLoadedUrl() != null
|
| + && mFakeServer.getLoadedUrl().contains(CONTEXTUAL_SEARCH_PREFETCH_PARAM));
|
| + }
|
| +
|
| + private void assertLoadedNormalPriorityUrl() {
|
| + if (mFakeServer == null) return;
|
| + String message = "Expected a normal priority search request URL, but got "
|
| + + (mFakeServer.getLoadedUrl() != null ? mFakeServer.getLoadedUrl() : "null");
|
| + assertTrue(message, mFakeServer.getLoadedUrl() != null
|
| + && mFakeServer.getLoadedUrl().contains(NORMAL_PRIORITY_SEARCH_ENDPOINT));
|
| + assertTrue("Normal priority request should not have the prefetch parameter, but did!",
|
| + mFakeServer.getLoadedUrl() != null
|
| + && !mFakeServer.getLoadedUrl().contains(CONTEXTUAL_SEARCH_PREFETCH_PARAM));
|
| + }
|
| +
|
| + private void assertNoSearchesLoaded() {
|
| + assertEquals(0, mFakeServer.loadedUrlCount());
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + private void assertContentViewCoreCreated() {
|
| + assertTrue(mFakeServer.isSearchContentViewCreated());
|
| + }
|
| +
|
| + private void assertNoContentViewCore() {
|
| + assertFalse(mFakeServer.isSearchContentViewCreated());
|
| + }
|
| +
|
| + /**
|
| + * Fakes navigation of the Content View with the given httpResult code.
|
| + * The URL of the navigation is the one requested previously.
|
| + * @param httpResultCode The result to fake.
|
| + */
|
| + private void fakeContentViewDidNavigate(int httpResultCode) {
|
| + String url = mFakeServer.getLoadedUrl();
|
| + mFakeServer.handleDidNavigateMainFrame(url, httpResultCode);
|
| + }
|
| +
|
| + /**
|
| + * Waits for the Search Panel (the Search Bar) to peek up from the bottom, and asserts that it
|
| + * did peek.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForPanelToPeekAndAssert() throws InterruptedException {
|
| + assertTrue("Search Bar did not peek.", waitForPanelToEnterState(PanelState.PEEKED));
|
| + }
|
| +
|
| + /**
|
| + * Waits for the Search Panel to expand, and asserts that it did expand.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForPanelToExpandAndAssert() throws InterruptedException {
|
| + assertTrue("Search Bar did not expand.", waitForPanelToEnterState(PanelState.EXPANDED));
|
| + }
|
| +
|
| + /**
|
| + * Waits for the Search Panel to maximize, and asserts that it did maximize.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForPanelToMaximizeAndAssert() throws InterruptedException {
|
| + assertTrue("Search Bar did not maximize.", waitForPanelToEnterState(PanelState.MAXIMIZED));
|
| + }
|
| +
|
| + /**
|
| + * Waits for the Search Panel to close, and asserts that it did close.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForPanelToCloseAndAssert() throws InterruptedException {
|
| + // TODO(donnd): figure out why using waitForPanelToEnterState here doesn't work.
|
| + assertTrue("Search Bar did not close.",
|
| + CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + return !mManager.isSearchPanelShowing();
|
| + }
|
| + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL));
|
| + }
|
| +
|
| + /**
|
| + * Waits for the Search Panel to enter the given {@code PanelState}.
|
| + * @throws InterruptedException
|
| + */
|
| + private boolean waitForPanelToEnterState(final PanelState state) throws InterruptedException {
|
| + return CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + return mPanelDelegate != null
|
| + && mPanelDelegate.getPanelState() == state;
|
| + }
|
| + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL);
|
| + }
|
| +
|
| + /**
|
| + * Waits for the manager to finish processing a gesture.
|
| + * Tells the manager that a gesture has started, and then waits for it to complete.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForGestureProcessingAndAssert() throws InterruptedException {
|
| + assertTrue("Gesture processing did not complete.",
|
| + CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + return !mSelectionController.wasAnyTapGestureDetected();
|
| + }
|
| + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL));
|
| + }
|
| +
|
| + /**
|
| + * Shorthand for a common sequence:
|
| + * 1) Waits for gesture processing,
|
| + * 2) Waits for the panel to close,
|
| + * 3) Asserts that there is no selection and that the panel closed.
|
| + * @throws InterruptedException
|
| + */
|
| + private void waitForGestureToClosePanelAndAssertNoSelection() throws InterruptedException {
|
| + waitForGestureProcessingAndAssert();
|
| + waitForPanelToCloseAndAssert();
|
| + assertPanelClosedOrUndefined();
|
| + assertNull(getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Waits for the selected text string to be the given string, and asserts.
|
| + * @param text The string to wait for the selection to become.
|
| + */
|
| + private void waitForSelectionToBe(final String text) throws InterruptedException {
|
| + assertTrue("Bar never showed desired text.",
|
| + CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + return TextUtils.equals(text, getSelectedText());
|
| + }
|
| + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL));
|
| + }
|
| +
|
| + /**
|
| + * A ContentViewCore that has some methods stubbed out for testing.
|
| + */
|
| + private static final class StubbedContentViewCore extends ContentViewCore {
|
| + private boolean mIsFocusedNodeEditable;
|
| +
|
| + public StubbedContentViewCore(Context context) {
|
| + super(context);
|
| + }
|
| +
|
| + public void setIsFocusedNodeEditableForTest(boolean isFocusedNodeEditable) {
|
| + mIsFocusedNodeEditable = isFocusedNodeEditable;
|
| + }
|
| +
|
| + @Override
|
| + public boolean isFocusedNodeEditable() {
|
| + return mIsFocusedNodeEditable;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Generate a swipe sequence from the give start/end X,Y percentages, for the given steps.
|
| + * Works in either landscape or portrait orientation.
|
| + */
|
| + private void swipe(float startX, float startY, float endX, float endY, int stepCount) {
|
| + Point size = new Point();
|
| + getActivity().getWindowManager().getDefaultDisplay().getSize(size);
|
| + float dragStartX = size.x * startX;
|
| + float dragEndX = size.x * endX;
|
| + float dragStartY = size.y * startY;
|
| + float dragEndY = size.y * endY;
|
| + long downTime = SystemClock.uptimeMillis();
|
| + dragStart(dragStartX, dragStartY, downTime);
|
| + dragTo(dragStartX, dragEndX, dragStartY, dragEndY, stepCount, downTime);
|
| + dragEnd(dragEndX, dragEndY, downTime);
|
| + }
|
| +
|
| + /**
|
| + * Swipes the panel up to it's expanded size.
|
| + */
|
| + private void swipePanelUp() {
|
| + swipe(0.5f, 0.95f, 0.5f, 0.55f, 1000);
|
| + }
|
| +
|
| + /**
|
| + * Swipes the panel up to it's maximized size.
|
| + */
|
| + private void swipePanelUpToTop() {
|
| + swipe(0.5f, 0.95f, 0.5f, 0.05f, 1000);
|
| + }
|
| +
|
| + /**
|
| + * Scrolls the base page.
|
| + */
|
| + private void scrollBasePage() {
|
| + swipe(0.f, 0.75f, 0.f, 0.7f, 100);
|
| + }
|
| +
|
| + /**
|
| + * Taps the base page near the top.
|
| + */
|
| + private void tapBasePage() throws InterruptedException {
|
| + tapBasePage(0.1f, 0.1f);
|
| + }
|
| +
|
| + /**
|
| + * Taps the base page at the given x, y position.
|
| + */
|
| + private void tapBasePage(float x, float y) throws InterruptedException {
|
| + Point size = new Point();
|
| + getActivity().getWindowManager().getDefaultDisplay().getSize(size);
|
| + x *= size.x;
|
| + y *= size.y;
|
| + singleClick(x, y);
|
| + waitForPanelToCloseAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Click various places to cause the panel to show, expand, then close.
|
| + */
|
| + private void clickToExpandAndClosePanel() throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + tapPeekingBarToExpandAndAssert();
|
| + tapBasePage();
|
| + waitForPanelToCloseAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Generate a click in the panel's bar.
|
| + * @barHeight The vertical position where the click should take place as a percentage
|
| + * of the screen size.
|
| + */
|
| + private void clickPanelBar(float barPositionVertical) {
|
| + Point size = new Point();
|
| + getActivity().getWindowManager().getDefaultDisplay().getSize(size);
|
| + float w = size.x;
|
| + float h = size.y;
|
| + boolean landscape = w > h;
|
| + float tapX = landscape ? w * barPositionVertical : w / 2f;
|
| + float tapY = landscape ? h / 2f : h * barPositionVertical;
|
| +
|
| + singleClick(tapX, tapY);
|
| + }
|
| +
|
| + /**
|
| + * Taps the peeking bar to expand the panel
|
| + */
|
| + private void tapPeekingBarToExpandAndAssert() throws InterruptedException {
|
| + clickPanelBar(0.95f);
|
| + waitForPanelToExpandAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Simple sequence useful for checking if a Search Term Resolution request is sent.
|
| + * Resets the fake server and clicks near to cause a search, then clicks far to let the panel
|
| + * drop down (taking us back to the same state).
|
| + */
|
| + private void clickToTriggerSearchTermResolution()
|
| + throws InterruptedException, TimeoutException {
|
| + mFakeServer.reset();
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * Simple sequence useful for checking if a Search Request is prefetched.
|
| + * Resets the fake server and clicks near to cause a search, fakes a server response to
|
| + * trigger a prefetch, then clicks far to let the panel drop down
|
| + * which takes us back to the starting state except the the fake server knows
|
| + * if a prefetch occurred.
|
| + */
|
| + private void clickToTriggerPrefetch() throws InterruptedException, TimeoutException {
|
| + mFakeServer.reset();
|
| + clickWordNode("states");
|
| + assertSearchTermRequested();
|
| + fakeResponse(false, 200, "States", "display-text", "alternate-term", false);
|
| + waitForPanelToPeekAndAssert();
|
| + clickNode("states-far");
|
| + }
|
| +
|
| + /**
|
| + * Simple sequence to click, resolve, and prefetch. Verifies a prefetch occurred.
|
| + */
|
| + private void clickToResolveAndAssertPrefetch() throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertLoadedNoUrl();
|
| + assertSearchTermRequested();
|
| +
|
| + fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + assertContainsParameters("states", "alternate-term");
|
| + }
|
| +
|
| + /**
|
| + * Resets the tap counters on the UI thread.
|
| + */
|
| + private void resetTapCounters() throws InterruptedException {
|
| + ThreadUtils.runOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mPolicy.resetTapCounters();
|
| + // The "Promo" tap counter is never reset outside of testing because it
|
| + // is used to persistently count the number of peeks *ever* seen by the user
|
| + // before the first open, and is then frozen in a disabled state to record that
|
| + // value rather than being reset.
|
| + // We reset it here to simulate a new user for our feature.
|
| + mPreferenceManager.setContextualSearchTapTriggeredPromoCount(0);
|
| + }
|
| + });
|
| + CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + return mPolicy.didResetTapCounters();
|
| + }
|
| + }, TEST_TIMEOUT, DEFAULT_POLLING_INTERVAL);
|
| + }
|
| +
|
| + /**
|
| + * Tests whether the contextual search panel hides when omnibox is clicked.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testHidesWhenOmniboxFocused() throws InterruptedException, TimeoutException {
|
| + clickWordNode("intelligence");
|
| +
|
| + assertEquals("Intelligence", mFakeServer.getSearchTermRequested());
|
| + fakeResponse(false, 200, "Intelligence", "display-text", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + waitForPanelToPeekAndAssert();
|
| +
|
| + OmniboxTestUtils.toggleUrlBarFocus((UrlBar) getActivity().findViewById(R.id.url_bar), true);
|
| +
|
| + assertPanelClosedOrUndefined();
|
| + }
|
| +
|
| + /**
|
| + * Tests the doesContainAWord method.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testDoesContainAWord() {
|
| + assertTrue(mManager.doesContainAWord("word"));
|
| + assertTrue(mManager.doesContainAWord("word "));
|
| + assertFalse("Emtpy string should not be considered a word!",
|
| + mManager.doesContainAWord(""));
|
| + assertFalse("Special symbols should not be considered a word!",
|
| + mManager.doesContainAWord("@"));
|
| + assertFalse("White space should not be considered a word",
|
| + mManager.doesContainAWord(" "));
|
| + assertTrue(mManager.doesContainAWord("Q2"));
|
| + assertTrue(mManager.doesContainAWord("123"));
|
| + }
|
| +
|
| + /**
|
| + * Tests the isValidSelection method.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testIsValidSelection() {
|
| + StubbedContentViewCore stubbedCvc = new StubbedContentViewCore(
|
| + getActivity().getBaseContext());
|
| + assertTrue(mManager.isValidSelection("valid", stubbedCvc));
|
| + assertFalse(mManager.isValidSelection(" ", stubbedCvc));
|
| + stubbedCvc.setIsFocusedNodeEditableForTest(true);
|
| + assertFalse(mManager.isValidSelection("editable", stubbedCvc));
|
| + stubbedCvc.setIsFocusedNodeEditableForTest(false);
|
| + String numberString = "0123456789";
|
| + StringBuilder longStringBuilder = new StringBuilder();
|
| + for (int i = 0; i < 11; i++) {
|
| + longStringBuilder.append(numberString);
|
| + }
|
| + assertTrue(mManager.isValidSelection(numberString, stubbedCvc));
|
| + assertFalse(mManager.isValidSelection(longStringBuilder.toString(),
|
| + stubbedCvc));
|
| + }
|
| +
|
| + /**
|
| + * Tests a simple Tap.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTap() throws InterruptedException, TimeoutException {
|
| + clickWordNode("intelligence");
|
| +
|
| + assertEquals("Intelligence", mFakeServer.getSearchTermRequested());
|
| + fakeResponse(false, 200, "Intelligence", "display-text", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests a simple Long-Press gesture, without opening the panel.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testLongPress() throws InterruptedException, TimeoutException {
|
| + longPressNode("states");
|
| +
|
| + assertNull(mFakeServer.getSearchTermRequested());
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedNoUrl();
|
| + assertNoContentViewCore();
|
| + }
|
| +
|
| + /**
|
| + * Tests swiping the overlay open, after an initial tap that activates the peeking card.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testSwipeExpand() throws InterruptedException, TimeoutException {
|
| + assertNoSearchesLoaded();
|
| + clickWordNode("intelligence");
|
| + assertNoSearchesLoaded();
|
| +
|
| + // Fake a search term resolution response.
|
| + fakeResponse(false, 200, "Intelligence", "United States Intelligence", "alternate-term",
|
| + false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertLoadedLowPriorityUrl();
|
| +
|
| + waitForPanelToPeekAndAssert();
|
| + swipePanelUp();
|
| + waitForPanelToExpandAndAssert();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests swiping the overlay open, after an initial long-press that activates the peeking card,
|
| + * followed by closing the panel.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testLongPressSwipeExpand() throws InterruptedException, TimeoutException {
|
| + longPressNode("intelligence");
|
| + assertNoContentViewCore();
|
| +
|
| + // Fake a search term resolution response.
|
| + fakeResponse(false, 200, "Intelligence", "United States Intelligence", "alternate-term",
|
| + false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| +
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedNoUrl();
|
| + assertNoContentViewCore();
|
| + swipePanelUp();
|
| + waitForPanelToExpandAndAssert();
|
| + assertContentViewCoreCreated();
|
| + assertLoadedNormalPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // tap the base page to close.
|
| + tapBasePage();
|
| + waitForPanelToCloseAndAssert();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertNoContentViewCore();
|
| + }
|
| +
|
| + /**
|
| + * Tests a sequence in landscape orientation: swiping the overlay open, after an
|
| + * initial tap that activates the peeking card.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testSwipeExpandLandscape() throws InterruptedException, TimeoutException {
|
| + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
| + testSwipeExpand();
|
| + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
| + }
|
| +
|
| + /**
|
| + * Tests tap to expand, after an initial tap to activate the peeking card.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapExpand() throws InterruptedException, TimeoutException {
|
| + assertNoSearchesLoaded();
|
| + clickWordNode("states");
|
| + assertNoContentViewCore();
|
| + assertNoSearchesLoaded();
|
| +
|
| + // Fake a search term resolution response.
|
| + fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
|
| + assertContainsParameters("states", "alternate-term");
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertLoadedLowPriorityUrl();
|
| + assertContentViewCoreCreated();
|
| + tapPeekingBarToExpandAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // tap the base page to close.
|
| + tapBasePage();
|
| + waitForPanelToCloseAndAssert();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertNoContentViewCore();
|
| + }
|
| +
|
| + /**
|
| + * Tests that only a single low-priority request is issued for a Tap/Open sequence.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapCausesOneLowPriorityRequest() throws InterruptedException, TimeoutException {
|
| + mFakeServer.reset();
|
| + clickWordNode("states");
|
| +
|
| + // We should not make a second-request until we get a good response from the first-request.
|
| + assertLoadedNoUrl();
|
| + assertEquals(0, mFakeServer.loadedUrlCount());
|
| + fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // When the second request succeeds, we should not issue a new request.
|
| + fakeContentViewDidNavigate(200);
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // When the bar opens, we should not make any additional request.
|
| + tapPeekingBarToExpandAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a failover for a prefetch request is issued after the panel is opened.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testPrefetchFailoverRequestMadeAfterOpen()
|
| + throws InterruptedException, TimeoutException {
|
| + mFakeServer.reset();
|
| + clickWordNode("states");
|
| +
|
| + // We should not make a SERP request until we get a good response from the resolve request.
|
| + assertLoadedNoUrl();
|
| + assertEquals(0, mFakeServer.loadedUrlCount());
|
| + fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // When the second request fails, we should not issue a new request.
|
| + fakeContentViewDidNavigate(500);
|
| + assertLoadedLowPriorityUrl();
|
| + assertEquals(1, mFakeServer.loadedUrlCount());
|
| +
|
| + // Once the bar opens, we make a new request at normal priority.
|
| + tapPeekingBarToExpandAndAssert();
|
| + assertLoadedNormalPriorityUrl();
|
| + assertEquals(2, mFakeServer.loadedUrlCount());
|
| + }
|
| +
|
| + /**
|
| + * Tests a simple Tap with disable-preload set.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapDisablePreload() throws InterruptedException, TimeoutException {
|
| + clickWordNode("intelligence");
|
| +
|
| + assertSearchTermRequested();
|
| + fakeResponse(false, 200, "Intelligence", "display-text", "alternate-term", true);
|
| + assertContainsNoParameters();
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that long-press selects text, and a subsequent tap will unselect text.
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testLongPressGestureSelects() throws InterruptedException, TimeoutException {
|
| + longPressNode("intelligence");
|
| + assertEquals("Intelligence", getSelectedText());
|
| + fakeResponse(false, 200, "Intelligence", "Intelligence", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedNoUrl(); // No load after long-press until opening panel.
|
| + clickNode("question-mark");
|
| + waitForGestureProcessingAndAssert();
|
| + assertNull(getSelectedText());
|
| + assertPanelClosedOrUndefined();
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture selects the expected text.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureSelects() throws InterruptedException, TimeoutException {
|
| + clickWordNode("intelligence");
|
| + assertEquals("Intelligence", getSelectedText());
|
| + fakeResponse(false, 200, "Intelligence", "Intelligence", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + clickNode("question-mark");
|
| + waitForGestureProcessingAndAssert();
|
| + assertNull(getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture on a special character does not select or show the panel.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureOnSpecialCharacterDoesntSelect()
|
| + throws InterruptedException, TimeoutException {
|
| + clickNode("question-mark");
|
| + waitForGestureProcessingAndAssert();
|
| + assertNull(getSelectedText());
|
| + assertPanelClosedOrUndefined();
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture followed by scrolling clears the selection.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureFollowedByScrollClearsSelection()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("intelligence");
|
| + fakeResponse(false, 200, "Intelligence", "Intelligence", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| + waitForPanelToPeekAndAssert();
|
| + assertLoadedLowPriorityUrl();
|
| + scrollBasePage();
|
| + assertPanelClosedOrUndefined();
|
| + assertNull(mSelectionController.getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture followed by tapping an invalid character doesn't select.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureFollowedByInvalidTextTapCloses()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states-far");
|
| + waitForPanelToPeekAndAssert();
|
| + clickNode("question-mark");
|
| + waitForGestureProcessingAndAssert();
|
| + assertPanelClosedOrUndefined();
|
| + assertNull(mSelectionController.getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture followed by tapping a non-text character doesn't select.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureFollowedByNonTextTap()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states-far");
|
| + waitForPanelToPeekAndAssert();
|
| + clickNode("button");
|
| + waitForGestureProcessingAndAssert();
|
| + assertPanelClosedOrUndefined();
|
| + assertNull(mSelectionController.getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture far away toggles selecting text.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGestureFarAwayTogglesSelecting()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertEquals("States", getSelectedText());
|
| + waitForPanelToPeekAndAssert();
|
| + clickNode("states-far");
|
| + waitForGestureProcessingAndAssert();
|
| + assertNull(getSelectedText());
|
| + assertPanelClosedOrUndefined();
|
| + clickNode("states-far");
|
| + waitForGestureProcessingAndAssert();
|
| + waitForPanelToPeekAndAssert();
|
| + assertEquals("States", getSelectedText());
|
| + }
|
| +
|
| + /**
|
| + * Tests that sequential Tap gestures nearby keep selecting.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapGesturesNearbyKeepSelecting()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertEquals("States", getSelectedText());
|
| + waitForPanelToPeekAndAssert();
|
| + // Because sequential taps never hide the bar, we we can't wait for it to peek.
|
| + // Instead we use clickNode (which doesn't wait) instead of clickWordNode and wait
|
| + // for the selection to change.
|
| + clickNode("states-near");
|
| + waitForSelectionToBe("StatesNear");
|
| + clickNode("states");
|
| + waitForSelectionToBe("States");
|
| + }
|
| +
|
| + /**
|
| + * Tests that a long-press gesture followed by scrolling does not clear the selection.
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testLongPressGestureFollowedByScrollMaintainsSelection()
|
| + throws InterruptedException, TimeoutException {
|
| + longPressNode("intelligence");
|
| + waitForPanelToPeekAndAssert();
|
| + scrollBasePage();
|
| + assertPanelClosedOrUndefined();
|
| + assertEquals("Intelligence", getSelectedText());
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a long-press gesture followed by a tap does not select.
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testLongPressGestureFollowedByTapDoesntSelect()
|
| + throws InterruptedException, TimeoutException {
|
| + longPressNode("intelligence");
|
| + waitForPanelToPeekAndAssert();
|
| + clickWordNode("states-far");
|
| + waitForGestureToClosePanelAndAssertNoSelection();
|
| + assertLoadedNoUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that the panel closes when its base page crashes.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testContextualSearchDismissedOnForegroundTabCrash()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertEquals("States", getSelectedText());
|
| + waitForPanelToPeekAndAssert();
|
| +
|
| + ThreadUtils.runOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + getActivity().getActivityTab().simulateRendererKilledForTesting(true);
|
| + }
|
| + });
|
| +
|
| + // Give the panelState time to change
|
| + CriteriaHelper.pollForCriteria(new Criteria(){
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + PanelState panelState = mPanelDelegate.getPanelState();
|
| + return panelState != PanelState.PEEKED;
|
| + }
|
| + });
|
| +
|
| + assertPanelClosedOrUndefined();
|
| + }
|
| +
|
| + /**
|
| + * Test the the panel does not close when some background tab crashes.
|
| + */
|
| + @CommandLineFlags.Add(ChromeSwitches.DISABLE_DOCUMENT_MODE)
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testContextualSearchNotDismissedOnBackgroundTabCrash()
|
| + throws InterruptedException, TimeoutException {
|
| + ChromeTabUtils.newTabFromMenu(getInstrumentation(),
|
| + (ChromeTabbedActivity) getActivity());
|
| + final Tab tab2 = TabModelUtils.getCurrentTab(getActivity().getCurrentTabModel());
|
| +
|
| + ThreadUtils.runOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + TabModelUtils.setIndex(getActivity().getCurrentTabModel(), 0);
|
| + }
|
| + });
|
| + getInstrumentation().waitForIdleSync();
|
| +
|
| + clickWordNode("states");
|
| + assertEquals("States", getSelectedText());
|
| + waitForPanelToPeekAndAssert();
|
| +
|
| + ThreadUtils.runOnUiThread(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + tab2.simulateRendererKilledForTesting(false);
|
| + }
|
| + });
|
| +
|
| + // Give the panelState time to change
|
| + CriteriaHelper.pollForCriteria(new Criteria(){
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + PanelState panelState = mPanelDelegate.getPanelState();
|
| + return panelState != PanelState.PEEKED;
|
| + }
|
| + });
|
| +
|
| + waitForPanelToPeekAndAssert();
|
| + }
|
| +
|
| + /*
|
| + * Test that tapping on the Search Bar before having a resolved search term does not
|
| + * promote to a tab, and that after the resolution it does promote to a tab.
|
| + *
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + */
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + @CommandLineFlags.Add(ChromeSwitches.DISABLE_DOCUMENT_MODE)
|
| + @FlakyTest
|
| + public void testTapSearchBarPromotesToTab() throws InterruptedException, TimeoutException {
|
| + // Tap on a word.
|
| + clickWordNode("intelligence");
|
| + assertSearchTermRequested();
|
| +
|
| + // Wait for the panel to peek.
|
| + waitForPanelToPeekAndAssert();
|
| +
|
| + // Swipe Panel up and wait for it to maximize.
|
| + swipePanelUpToTop();
|
| + waitForPanelToMaximizeAndAssert();
|
| +
|
| + // Create an observer to track that a new tab is created.
|
| + final CallbackHelper tabCreatedHelper = new CallbackHelper();
|
| + int tabCreatedHelperCallCount = tabCreatedHelper.getCallCount();
|
| + TabModelSelectorObserver observer = new EmptyTabModelSelectorObserver() {
|
| + @Override
|
| + public void onNewTabCreated(Tab tab) {
|
| + tabCreatedHelper.notifyCalled();
|
| + }
|
| + };
|
| + getActivity().getTabModelSelector().addObserver(observer);
|
| +
|
| + // Tap the Search Bar.
|
| + clickPanelBar(0.05f);
|
| +
|
| + // The Search Term Resolution response hasn't arrived yet, so the Panel should not
|
| + // be promoted. Therefore, we are asserting that the Panel is still maximized.
|
| + waitForPanelToMaximizeAndAssert();
|
| +
|
| + // Get a Search Term Resolution response.
|
| + fakeResponse(false, 200, "Intelligence", "display-text", "alternate-term", false);
|
| + assertContainsParameters("Intelligence", "alternate-term");
|
| +
|
| + // Tap the Search Bar again.
|
| + clickPanelBar(0.05f);
|
| +
|
| + // Now that the response has arrived, tapping on the Search Panel should promote it
|
| + // to a Tab. Therefore, we are asserting that the Panel got closed.
|
| + waitForPanelToCloseAndAssert();
|
| +
|
| + // Finally, make sure a tab was created.
|
| + if (!FeatureUtilities.isDocumentMode(getInstrumentation().getContext())) {
|
| + // TODO(donnd): figure out how to check for tab creation in Document mode.
|
| + tabCreatedHelper.waitForCallback(tabCreatedHelperCallCount);
|
| + }
|
| + getActivity().getTabModelSelector().removeObserver(observer);
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture on an element with an ARIA role does not trigger.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapOnRoleIgnored() throws InterruptedException, TimeoutException {
|
| + clickNode("role");
|
| + waitForGestureToClosePanelAndAssertNoSelection();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture on an element with an ARIA attribute does not trigger.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapOnARIAIgnored() throws InterruptedException, TimeoutException {
|
| + clickNode("aria");
|
| + waitForGestureToClosePanelAndAssertNoSelection();
|
| + }
|
| +
|
| + /**
|
| + * Tests that a Tap gesture on an element that is focusable does not trigger.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testTapOnFocusableIgnored() throws InterruptedException, TimeoutException {
|
| + clickNode("focusable");
|
| + waitForGestureToClosePanelAndAssertNoSelection();
|
| + }
|
| +
|
| + /**
|
| + * Tests that taps can be resolve-limited for decided users.
|
| + * @CommandLineFlags.Add(
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_RESOLVE_LIMIT_FOR_DECIDED + "=2")
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + * crbug.com/487759
|
| + */
|
| + @FlakyTest
|
| + public void testTapResolveLimitForDecided() throws InterruptedException, TimeoutException {
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + // 3rd click should not resolve.
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermNotRequested();
|
| +
|
| + // Expanding the panel should reset the limit.
|
| + clickToExpandAndClosePanel();
|
| +
|
| + // Click should resolve again.
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + }
|
| +
|
| + /**
|
| + * Tests that taps can be resolve-limited for undecided users.
|
| + * @CommandLineFlags.Add({
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_RESOLVE_LIMIT_FOR_UNDECIDED + "=2"})
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + * crbug.com/487759
|
| + */
|
| + @FlakyTest
|
| + public void testTapResolveLimitForUndecided() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(false);
|
| +
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + // 3rd click should not resolve.
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermNotRequested();
|
| +
|
| + // Expanding the panel should reset the limit.
|
| + clickToExpandAndClosePanel();
|
| +
|
| + // Click should resolve again.
|
| + clickToTriggerSearchTermResolution();
|
| + assertSearchTermRequested();
|
| + }
|
| +
|
| + /**
|
| + * Tests that taps can be preload-limited for decided users.
|
| + * @CommandLineFlags.Add(
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_PREFETCH_LIMIT_FOR_DECIDED + "=2")
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + * crbug.com/487759
|
| + */
|
| + @FlakyTest
|
| + public void testTapPrefetchLimitForDecided() throws InterruptedException, TimeoutException {
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + // 3rd click should not preload.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedNoUrl();
|
| +
|
| + // Expanding the panel should reset the limit.
|
| + clickToExpandAndClosePanel();
|
| +
|
| + // Click should preload again.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests that taps can be preload-limited for undecided users.
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + * @CommandLineFlags.Add(
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_PREFETCH_LIMIT_FOR_UNDECIDED + "=2")
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * crbug.com/487759
|
| + */
|
| + @FlakyTest
|
| + public void testTapPrefetchLimitForUndecided() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(false);
|
| +
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + // 3rd click should not preload.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedNoUrl();
|
| +
|
| + // Expanding the panel should reset the limit.
|
| + clickToExpandAndClosePanel();
|
| +
|
| + // Click should preload again.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests the tap triggered promo limit for opt-out.
|
| + * @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + * @CommandLineFlags.Add({
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_PROMO_ON_LIMITED_TAPS + "=true",
|
| + * ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_TRIGGERED_PROMO_LIMIT + "=2"})
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + * crbug.com/487759
|
| + */
|
| + @FlakyTest
|
| + public void testTapTriggeredPromoLimitForOptOut()
|
| + throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(false);
|
| +
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| +
|
| + // 3rd click won't peek the panel.
|
| + clickNode("states");
|
| + assertPanelClosedOrUndefined();
|
| + // The Tap should not select any text either!
|
| + assertNull(getSelectedText());
|
| +
|
| + // A long-press should still show the promo bar.
|
| + longPressNode("states");
|
| + waitForPanelToPeekAndAssert();
|
| +
|
| + // Expanding the panel should deactivate the limit.
|
| + clickToExpandAndClosePanel();
|
| +
|
| + // Three taps should work now.
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| + clickWordNode("states");
|
| + clickNode("states-far");
|
| + waitForPanelToCloseAndAssert();
|
| + }
|
| +
|
| + /**
|
| + * This is a test that happens to create a separate bar without any content area!
|
| + *
|
| + * @SmallTest
|
| + * @Feature({"ContextualSearch"})
|
| + */
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + @FlakyTest
|
| + @CommandLineFlags.Add(
|
| + ContextualSearchFieldTrial.CONTEXTUAL_SEARCH_TAP_PREFETCH_LIMIT_FOR_DECIDED + "=2")
|
| + public void testDisembodiedBar() throws InterruptedException, TimeoutException {
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + // 3rd click should not preload.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedNoUrl();
|
| +
|
| + // Expanding the panel should reset the limit.
|
| + swipePanelUp();
|
| + singleClick(0.5f, 0.5f);
|
| + waitForPanelToCloseAndAssert();
|
| +
|
| + // Click should preload again.
|
| + clickToTriggerPrefetch();
|
| + assertLoadedLowPriorityUrl();
|
| + }
|
| +
|
| + /**
|
| + * Tests expanding the panel before the search term has resolved, verifies that nothing
|
| + * loads until the resolve completes and that it's now a normal priority URL.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testExpandBeforeSearchTermResolution()
|
| + throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertNoContentViewCore();
|
| +
|
| + // Expanding before the search term resolves should not load anything.
|
| + tapPeekingBarToExpandAndAssert();
|
| + assertLoadedNoUrl();
|
| +
|
| + // Once the response comes in, it should load.
|
| + fakeResponse(false, 200, "states", "United States Intelligence", "alternate-term", false);
|
| + assertContainsParameters("states", "alternate-term");
|
| + assertLoadedNormalPriorityUrl();
|
| + assertContentViewCoreCreated();
|
| + }
|
| +
|
| + /**
|
| + * Tests that an error from the Search Term Resolution request causes a fallback to a
|
| + * search request for the literal selection.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testSearchTermResolutionError() throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + assertSearchTermRequested();
|
| + fakeResponse(false, 403, "", "", "", false);
|
| + assertLoadedNoUrl();
|
| + tapPeekingBarToExpandAndAssert();
|
| + assertLoadedNormalPriorityUrl();
|
| + }
|
| +
|
| + // --------------------------------------------------------------------------------------------
|
| + // HTTP/HTTPS for Undecided/Decided users.
|
| + // --------------------------------------------------------------------------------------------
|
| +
|
| + /**
|
| + * Tests that HTTPS does not resolve in the opt-out model before the user accepts.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testHttpsBeforeAcceptForOptOut() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(false);
|
| + mFakeServer.setShouldUseHttps(true);
|
| +
|
| + clickWordNode("states");
|
| + assertLoadedLowPriorityUrl();
|
| + assertSearchTermNotRequested();
|
| + }
|
| +
|
| + /**
|
| + * Tests that HTTPS does resolve in the opt-out model after the user accepts.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testHttpsAfterAcceptForOptOut() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(true);
|
| + mFakeServer.setShouldUseHttps(true);
|
| +
|
| + clickToResolveAndAssertPrefetch();
|
| + }
|
| +
|
| + /**
|
| + * Tests that HTTP does resolve in the opt-out model before the user accepts.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testHttpBeforeAcceptForOptOut() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(false);
|
| +
|
| + clickToResolveAndAssertPrefetch();
|
| + }
|
| +
|
| + /**
|
| + * Tests that HTTP does resolve in the opt-out model after the user accepts.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testHttpAfterAcceptForOptOut() throws InterruptedException, TimeoutException {
|
| + mPolicy.overrideDecidedStateForTesting(true);
|
| +
|
| + clickToResolveAndAssertPrefetch();
|
| + }
|
| +
|
| + // --------------------------------------------------------------------------------------------
|
| + // App Menu Suppression
|
| + // --------------------------------------------------------------------------------------------
|
| +
|
| + /**
|
| + * Simulates pressing the App Menu button.
|
| + */
|
| + private void pressAppMenuKey() {
|
| + pressKey(KeyEvent.KEYCODE_MENU);
|
| + }
|
| +
|
| + /**
|
| + * Asserts whether the App Menu is visible.
|
| + */
|
| + private void assertAppMenuVisibility(final boolean isVisible) throws InterruptedException {
|
| + assertTrue(CriteriaHelper.pollForCriteria(new Criteria() {
|
| + @Override
|
| + public boolean isSatisfied() {
|
| + if (((CompositorChromeActivity) getActivity())
|
| + .getAppMenuHandler().isAppMenuShowing() == isVisible) return true;
|
| + return false;
|
| + }
|
| + }));
|
| + }
|
| +
|
| + /**
|
| + * Tests that the App Menu gets suppressed when Search Panel is expanded.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testAppMenuSuppressedWhenExpanded() throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + tapPeekingBarToExpandAndAssert();
|
| +
|
| + pressAppMenuKey();
|
| + assertAppMenuVisibility(false);
|
| +
|
| + tapBasePage();
|
| + waitForPanelToCloseAndAssert();
|
| +
|
| + pressAppMenuKey();
|
| + assertAppMenuVisibility(true);
|
| + }
|
| +
|
| + /**
|
| + * Tests that the App Menu gets suppressed when Search Panel is maximized.
|
| + */
|
| + @SmallTest
|
| + @Feature({"ContextualSearch"})
|
| + @Restriction({RESTRICTION_TYPE_PHONE, RESTRICTION_TYPE_NON_LOW_END_DEVICE})
|
| + public void testAppMenuSuppressedWhenMaximized() throws InterruptedException, TimeoutException {
|
| + clickWordNode("states");
|
| + swipePanelUpToTop();
|
| + waitForPanelToMaximizeAndAssert();
|
| +
|
| + pressAppMenuKey();
|
| + assertAppMenuVisibility(false);
|
| +
|
| + pressBackButton();
|
| + waitForPanelToCloseAndAssert();
|
| +
|
| + pressAppMenuKey();
|
| + assertAppMenuVisibility(true);
|
| + }
|
| +}
|
|
|