// Copyright 2018 The Chromium Authors
// 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.omnibox;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.not;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import android.app.Activity;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ImageButton;
import androidx.core.view.MarginLayoutParamsCompat;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.chromium.base.MathUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Matchers;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.toolbar.LocationBarModel;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.OmniboxTestUtils;
import org.chromium.content_public.browser.test.util.ClickUtils;
import org.chromium.ui.permissions.AndroidPermissionDelegate;
import org.chromium.ui.test.util.UiRestriction;
import java.util.concurrent.Callable;
/** Unit tests for {@link LocationBarLayout}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class LocationBarLayoutTest {
private static final String SEARCH_TERMS = "machine learning";
private static final String SEARCH_TERMS_URL = "testing.com";
@Rule
public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock AndroidPermissionDelegate mAndroidPermissionDelegate;
private OmniboxTestUtils mOmnibox;
public static final LocationBarModel.OfflineStatus OFFLINE_STATUS =
new LocationBarModel.OfflineStatus() {
@Override
public boolean isShowingTrustedOfflinePage(Tab tab) {
return false;
}
@Override
public boolean isOfflinePage(Tab tab) {
return false;
}
};
@Before
public void setUp() throws InterruptedException {
mActivityTestRule.startMainActivityOnBlankPage();
mOmnibox = new OmniboxTestUtils(mActivityTestRule.getActivity());
doReturn(true).when(mAndroidPermissionDelegate).hasPermission(anyString());
mActivityTestRule
.getActivity()
.getWindowAndroid()
.setAndroidPermissionDelegate(mAndroidPermissionDelegate);
}
private String getUrlText(UrlBar urlBar) {
return ThreadUtils.runOnUiThreadBlocking(() -> urlBar.getText().toString());
}
private UrlBar getUrlBar() {
return (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar);
}
private LocationBarLayout getLocationBar() {
return (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar);
}
private LocationBarMediator getLocationBarMediator() {
LocationBarCoordinator locationBarCoordinator =
(LocationBarCoordinator)
mActivityTestRule.getActivity().getToolbarManager().getLocationBar();
return locationBarCoordinator.getMediatorForTesting();
}
private ImageButton getDeleteButton() {
return mActivityTestRule.getActivity().findViewById(R.id.delete_button);
}
private ImageButton getMicButton() {
return mActivityTestRule.getActivity().findViewById(R.id.mic_button);
}
private View getStatusIconView() {
return mActivityTestRule.getActivity().findViewById(R.id.location_bar_status_icon_frame);
}
private void setUrlBarTextAndFocus(String text) {
final UrlBar urlBar = getUrlBar();
ThreadUtils.runOnUiThreadBlocking(
() -> {
urlBar.requestFocus();
});
CriteriaHelper.pollUiThread(() -> urlBar.hasFocus());
ThreadUtils.runOnUiThreadBlocking(
new Callable<Void>() {
@Override
public Void call() throws InterruptedException {
mActivityTestRule.typeInOmnibox(text, false);
return null;
}
});
}
@Test
@SmallTest
@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
public void testNotShowingVoiceSearchButtonIfUrlBarContainsText() {
// When there is text, the delete button should be visible.
setUrlBarTextAndFocus("testing");
onView(withId(R.id.delete_button)).check(matches(isDisplayed()));
onView(withId(R.id.mic_button)).check(matches(not(isDisplayed())));
}
@Test
@SmallTest
@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
public void testShowingVoiceSearchButtonIfUrlBarIsEmpty() {
// When there's no text, the mic button should be visible.
setUrlBarTextAndFocus("");
onView(withId(R.id.mic_button)).check(matches(isDisplayed()));
onView(withId(R.id.delete_button)).check(matches(not(isDisplayed())));
}
@Test
@SmallTest
public void testDeleteButton() {
setUrlBarTextAndFocus("testing");
CriteriaHelper.pollUiThread(
() -> {
Criteria.checkThat(getDeleteButton().getVisibility(), Matchers.is(VISIBLE));
});
ClickUtils.clickButton(getDeleteButton());
CriteriaHelper.pollUiThread(
() -> {
Criteria.checkThat(getDeleteButton().getVisibility(), Matchers.not(VISIBLE));
});
Assert.assertEquals("", getUrlText(getUrlBar()));
}
@Test
@SmallTest
public void testSetUrlBarFocus() {
LocationBarMediator locationBarMediator = getLocationBarMediator();
Assert.assertEquals(
0, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
ThreadUtils.runOnUiThreadBlocking(
() -> {
locationBarMediator.setUrlBarFocus(
true, SEARCH_TERMS_URL, OmniboxFocusReason.FAKE_BOX_LONG_PRESS);
});
Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
Assert.assertTrue(getLocationBarMediator().didFocusUrlFromFakebox());
Assert.assertEquals(SEARCH_TERMS_URL, getUrlText(getUrlBar()));
Assert.assertEquals(
1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
ThreadUtils.runOnUiThreadBlocking(
() -> {
locationBarMediator.setUrlBarFocus(
true, SEARCH_TERMS, OmniboxFocusReason.SEARCH_QUERY);
});
Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
Assert.assertTrue(getLocationBarMediator().didFocusUrlFromFakebox());
Assert.assertEquals(SEARCH_TERMS, getUrlText(getUrlBar()));
Assert.assertEquals(
1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
ThreadUtils.runOnUiThreadBlocking(
() -> {
locationBarMediator.setUrlBarFocus(false, null, OmniboxFocusReason.UNFOCUS);
});
Assert.assertFalse(getLocationBarMediator().isUrlBarFocused());
Assert.assertFalse(getLocationBarMediator().didFocusUrlFromFakebox());
Assert.assertEquals(
1, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
ThreadUtils.runOnUiThreadBlocking(
() -> {
locationBarMediator.setUrlBarFocus(true, null, OmniboxFocusReason.OMNIBOX_TAP);
});
Assert.assertTrue(getLocationBarMediator().isUrlBarFocused());
Assert.assertFalse(getLocationBarMediator().didFocusUrlFromFakebox());
Assert.assertEquals(
2, RecordHistogram.getHistogramTotalCountForTesting("Android.OmniboxFocusReason"));
}
@Test
@MediumTest
public void testUpdateLayoutParams() {
LocationBarLayout locationBar = (LocationBarLayout) getLocationBar();
View statusIcon = getStatusIconView();
View urlContainer = getUrlBar();
ThreadUtils.runOnUiThreadBlocking(
() -> {
getUrlBar().requestFocus();
MarginLayoutParams urlLayoutParams =
(MarginLayoutParams) urlContainer.getLayoutParams();
MarginLayoutParamsCompat.setMarginEnd(
urlLayoutParams, /* very random, and only used to fail a check */
13047);
urlContainer.setLayoutParams(urlLayoutParams);
statusIcon.setVisibility(GONE);
locationBar.updateLayoutParams(
MeasureSpec.makeMeasureSpec(1000, MeasureSpec.EXACTLY));
urlLayoutParams = (MarginLayoutParams) urlContainer.getLayoutParams();
int endMarginNoIcon = MarginLayoutParamsCompat.getMarginEnd(urlLayoutParams);
MarginLayoutParamsCompat.setMarginEnd(
urlLayoutParams, /* very random, and only used to fail a check */
13047);
urlContainer.setLayoutParams(urlLayoutParams);
statusIcon.setVisibility(VISIBLE);
locationBar.updateLayoutParams(
MeasureSpec.makeMeasureSpec(1000, MeasureSpec.EXACTLY));
urlLayoutParams = (MarginLayoutParams) urlContainer.getLayoutParams();
int endMarginWithIcon = MarginLayoutParamsCompat.getMarginEnd(urlLayoutParams);
Assert.assertEquals(
endMarginNoIcon + locationBar.getEndPaddingPixelSizeOnFocusDelta(),
endMarginWithIcon);
});
}
@Test
@MediumTest
@DisabledTest(message = "flaky, see crbug.com/359597342")
public void testEnforceMinimumUrlBarWidth() {
setUrlBarTextAndFocus("");
ThreadUtils.runOnUiThreadBlocking(
() -> {
View urlBar = getUrlBar();
LocationBarLayout locationBar = getLocationBar();
int originalWidth = locationBar.getMeasuredWidth();
int constrainedWidth =
((MarginLayoutParams) urlBar.getLayoutParams()).getMarginStart()
+ locationBar
.getResources()
.getDimensionPixelSize(
R.dimen.location_bar_min_url_width);
int urlContainerMarginEnd =
((MarginLayoutParams) urlBar.getLayoutParams()).getMarginEnd();
locationBar.measure(
MeasureSpec.makeMeasureSpec(constrainedWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY));
Assert.assertEquals(
locationBar.findViewById(R.id.url_action_container).getVisibility(),
View.INVISIBLE);
locationBar.measure(
MeasureSpec.makeMeasureSpec(originalWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY));
Assert.assertEquals(
locationBar.findViewById(R.id.url_action_container).getVisibility(),
View.VISIBLE);
locationBar.measure(
MeasureSpec.makeMeasureSpec(
constrainedWidth + urlContainerMarginEnd - 1,
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY));
Assert.assertEquals(
locationBar.findViewById(R.id.url_action_container).getVisibility(),
View.INVISIBLE);
locationBar.setUrlActionContainerVisibility(VISIBLE);
Assert.assertEquals(
locationBar.findViewById(R.id.url_action_container).getVisibility(),
View.INVISIBLE);
});
}
@Test
@MediumTest
@Restriction({UiRestriction.RESTRICTION_TYPE_TABLET})
public void testTabletUrlBarTranslation_revampEnabled() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
LocationBarLayout locationBar = getLocationBar();
View urlBar = getUrlBar();
urlBar.requestFocus();
int marginStart =
((MarginLayoutParams) urlBar.getLayoutParams()).getMarginStart();
locationBar.updateLayoutParams(
MeasureSpec.makeMeasureSpec(
locationBar.getMeasuredWidth(), MeasureSpec.EXACTLY));
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 0,
/* urlFocusChangeFraction= */ MathUtils.EPSILON,
/* isUrlFocusChangeInProgress= */ true);
Assert.assertEquals(
marginStart + locationBar.getFocusedStatusViewSpacingDelta(),
((MarginLayoutParams) urlBar.getLayoutParams()).getMarginStart());
Assert.assertEquals(
locationBar.getFocusedStatusViewSpacingDelta()
* (-1 + MathUtils.EPSILON),
urlBar.getTranslationX(),
MathUtils.EPSILON);
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 0.5f,
/* urlFocusChangeFraction= */ 0.5f,
/* isUrlFocusChangeInProgress= */ false);
Assert.assertEquals(
locationBar.getFocusedStatusViewSpacingDelta() * -0.5,
urlBar.getTranslationX(),
MathUtils.EPSILON);
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 1.0f,
/* urlFocusChangeFraction= */ 1.0f,
/* isUrlFocusChangeInProgress= */ false);
Assert.assertEquals(0f, urlBar.getTranslationX(), MathUtils.EPSILON);
});
}
@Test
@MediumTest
@DisableFeatures(ChromeFeatureList.AVOID_RELAYOUT_DURING_FOCUS_ANIMATION)
@Restriction({UiRestriction.RESTRICTION_TYPE_TABLET})
public void testTabletUrlBarTranslation_revampEnabled_avoidRelayoutDisabled() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
LocationBarLayout locationBar = getLocationBar();
View urlBar = getUrlBar();
urlBar.requestFocus();
// Setting focus percent shouldn't crash.
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 0,
/* urlFocusChangeFraction= */ MathUtils.EPSILON,
/* isUrlFocusChangeInProgress= */ true);
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 0.5f,
/* urlFocusChangeFraction= */ 0.5f,
/* isUrlFocusChangeInProgress= */ false);
});
}
@Test
@MediumTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
public void testPhoneUrlBarAndStatusViewTranslation() {
ThreadUtils.runOnUiThreadBlocking(
() -> {
Activity activity = mActivityTestRule.getActivity();
int statusIconAndUrlBarOffset =
OmniboxResourceProvider.getToolbarSidePaddingForNtp(activity)
- OmniboxResourceProvider.getToolbarSidePadding(activity);
LocationBarLayout locationBar = getLocationBar();
View urlBar = getUrlBar();
View statusView = locationBar.findViewById(R.id.location_bar_status);
urlBar.requestFocus();
locationBar.updateLayoutParams(
MeasureSpec.makeMeasureSpec(
locationBar.getMeasuredWidth(), MeasureSpec.EXACTLY));
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 1,
/* urlFocusChangeFraction= */ MathUtils.EPSILON,
/* isUrlFocusChangeInProgress= */ true);
Assert.assertEquals(
statusIconAndUrlBarOffset * (1 - MathUtils.EPSILON),
urlBar.getTranslationX(),
MathUtils.EPSILON);
Assert.assertEquals(
OmniboxResourceProvider.getFocusedStatusViewLeftSpacing(activity)
+ statusIconAndUrlBarOffset * (1 - MathUtils.EPSILON),
statusView.getTranslationX(),
MathUtils.EPSILON);
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 1,
/* urlFocusChangeFraction= */ 0.5f,
/* isUrlFocusChangeInProgress= */ true);
Assert.assertEquals(
statusIconAndUrlBarOffset * 0.5,
urlBar.getTranslationX(),
MathUtils.EPSILON);
Assert.assertEquals(
OmniboxResourceProvider.getFocusedStatusViewLeftSpacing(activity)
+ statusIconAndUrlBarOffset * 0.5,
statusView.getTranslationX(),
MathUtils.EPSILON);
locationBar.setUrlFocusChangePercent(
/* ntpSearchBoxScrollFraction= */ 1.0f,
/* urlFocusChangeFraction= */ 1.0f,
/* isUrlFocusChangeInProgress= */ true);
Assert.assertEquals(0f, urlBar.getTranslationX(), MathUtils.EPSILON);
Assert.assertEquals(
OmniboxResourceProvider.getFocusedStatusViewLeftSpacing(activity),
statusView.getTranslationX(),
MathUtils.EPSILON);
});
}
}