Commit 9745d1e6 authored by Luca Hunkeler's avatar Luca Hunkeler Committed by Commit Bot

[Autofill Assistant] Add bottomsheet integration tests

Add integration test to check that the bottomsheet behaves correctly when minimized and restored.
Also verify that the page is showed as intended when the visual or layout viewport is resized.

Bug: b/143265578
Change-Id: If79e40b28ed885b70f353d787e05fb4b1a62f5fb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1890333
Commit-Queue: Luca Hunkeler <hluca@google.com>
Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Cr-Commit-Position: refs/heads/master@{#712540}
parent b86d3c32
......@@ -205,6 +205,7 @@ android_library("test_java") {
java_files = [
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantActionsCarouselUiTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantAutostartTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java",
......@@ -241,6 +242,7 @@ android_library("test_java") {
"//third_party/android_deps:com_google_protobuf_protobuf_lite_java",
"//third_party/android_support_test_runner:runner_java",
"//third_party/espresso:espresso_all_java",
"//third_party/feed:feed_lib_java",
"//third_party/hamcrest:hamcrest_java",
"//third_party/junit",
"//third_party/mockito:mockito_java",
......
// Copyright 2019 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.autofill_assistant;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getAbsoluteBoundingRect;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
import android.graphics.Rect;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.GeneralLocation;
import android.support.test.espresso.action.GeneralSwipeAction;
import android.support.test.espresso.action.Press;
import android.support.test.espresso.action.Swipe;
import android.support.test.filters.MediumTest;
import com.google.android.libraries.feed.common.functional.Function;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ChipType;
import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto.PeekMode;
import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureBottomSheetProto.ViewportResizing;
import org.chromium.chrome.browser.autofill_assistant.proto.ElementReferenceProto;
import org.chromium.chrome.browser.autofill_assistant.proto.FocusElementProto;
import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto.Choice;
import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import java.util.ArrayList;
import java.util.Collections;
/**
* Tests autofill assistant bottomsheet.
*/
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@RunWith(ChromeJUnit4ClassRunner.class)
public class AutofillAssistantBottomsheetTest {
@Rule
public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
private static final String TEST_PAGE = "/components/test/data/autofill_assistant/html/"
+ "bottomsheet_behaviour_target_website.html";
@Before
public void setUp() {
AutofillAssistantPreferencesUtil.setInitialPreferences(true);
mTestRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent(
InstrumentationRegistry.getTargetContext(),
mTestRule.getTestServer().getURL(TEST_PAGE)));
mTestRule.getActivity().getScrim().disableAnimationForTesting(true);
}
@Test
@MediumTest
public void testNoResize() throws Exception {
ArrayList<ActionProto> list = new ArrayList<>();
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("Hello world!")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Focus element"))))
.build());
// Set viewport resizing to NO_RESIZE.
list.add((ActionProto) ActionProto.newBuilder()
.setConfigureBottomSheet(
ConfigureBottomSheetProto.newBuilder()
.setViewportResizing(ViewportResizing.NO_RESIZE)
.setPeekMode(PeekMode.HANDLE))
.build());
// Focus on the bottom element.
list.add((ActionProto) ActionProto.newBuilder()
.setFocusElement(FocusElementProto.newBuilder().setElement(
ElementReferenceProto.newBuilder().addSelectors("p.bottom")))
.build());
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("NO_RESIZE")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Done"))))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
(SupportedScriptProto) SupportedScriptProto.newBuilder()
.setPath("bottomsheet_behaviour_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
ChipProto.newBuilder().setText("Bottomsheet behaviour")))
.build(),
list);
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script));
startAutofillAssistant(mTestRule.getActivity(), testService);
waitUntilViewMatchesCondition(withText("Hello world!"), isCompletelyDisplayed());
onView(withText("Focus element")).perform(click());
waitUntilViewMatchesCondition(withText("NO_RESIZE"), isCompletelyDisplayed());
checkElementIsCoveredByBottomsheet("bottom");
onView(withId(R.id.swipe_indicator)).perform(swipeDownToMinimize());
// Since no resizing of the viewport happens in this mode, the element is partially covered
// even when the bottomsheet is mimimized
checkElementIsCoveredByBottomsheet("bottom");
onView(withText("NO_RESIZE")).check(matches(not(isDisplayed())));
onView(withId(R.id.swipe_indicator)).perform(swipeUpToExpand());
checkElementIsCoveredByBottomsheet("bottom");
onView(withText("NO_RESIZE")).check(matches(isCompletelyDisplayed()));
}
@Test
@MediumTest
public void testResizeLayoutViewport() throws Exception {
ArrayList<ActionProto> list = new ArrayList<>();
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("Hello world!")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Focus element"))))
.build());
// Set viewport resizing to RESIZE_LAYOUT_VIEWPORT.
list.add((ActionProto) ActionProto.newBuilder()
.setConfigureBottomSheet(
ConfigureBottomSheetProto.newBuilder()
.setViewportResizing(
ViewportResizing.RESIZE_LAYOUT_VIEWPORT)
.setPeekMode(PeekMode.HANDLE))
.build());
// Focus on the bottom element.
list.add((ActionProto) ActionProto.newBuilder()
.setFocusElement(FocusElementProto.newBuilder().setElement(
ElementReferenceProto.newBuilder().addSelectors("p.bottom")))
.build());
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("RESIZE_LAYOUT_VIEWPORT")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Done"))))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
(SupportedScriptProto) SupportedScriptProto.newBuilder()
.setPath("bottomsheet_behaviour_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
ChipProto.newBuilder().setText("Bottomsheet behaviour")))
.build(),
list);
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script));
startAutofillAssistant(mTestRule.getActivity(), testService);
waitUntilViewMatchesCondition(withText("Hello world!"), isCompletelyDisplayed());
onView(withText("Focus element")).perform(click());
waitUntilViewMatchesCondition(withText("RESIZE_LAYOUT_VIEWPORT"), isCompletelyDisplayed());
checkElementIsCoveredByBottomsheet("bottom");
onView(withId(R.id.swipe_indicator)).perform(swipeDownToMinimize());
// Minimizing the bottomsheet should completely uncover the bottom element.
checkElementIsCoveredByBottomsheetByAtMost("bottom", 10);
onView(withText("RESIZE_LAYOUT_VIEWPORT")).check(matches(not(isDisplayed())));
onView(withId(R.id.swipe_indicator)).perform(swipeUpToExpand());
checkElementIsCoveredByBottomsheet("bottom");
onView(withText("RESIZE_LAYOUT_VIEWPORT")).check(matches(isCompletelyDisplayed()));
}
@Test
@MediumTest
public void testResizeVisualViewport() throws Exception {
ArrayList<ActionProto> list = new ArrayList<>();
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("Hello world!")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Focus element"))))
.build());
// Set viewport resizing to RESIZE_VISUAL_VIEWPORT.
list.add((ActionProto) ActionProto.newBuilder()
.setConfigureBottomSheet(
ConfigureBottomSheetProto.newBuilder()
.setViewportResizing(
ViewportResizing.RESIZE_VISUAL_VIEWPORT)
.setPeekMode(PeekMode.HANDLE))
.build());
// Focus on the bottom element.
list.add((ActionProto) ActionProto.newBuilder()
.setFocusElement(FocusElementProto.newBuilder().setElement(
ElementReferenceProto.newBuilder().addSelectors("p.bottom")))
.build());
// Prompt.
list.add((ActionProto) ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("RESIZE_VISUAL_VIEWPORT")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(ChipType.DONE_ACTION)
.setText("Done"))))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
(SupportedScriptProto) SupportedScriptProto.newBuilder()
.setPath("bottomsheet_behaviour_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
ChipProto.newBuilder().setText("Bottomsheet behaviour")))
.build(),
list);
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script));
startAutofillAssistant(mTestRule.getActivity(), testService);
waitUntilViewMatchesCondition(withText("Hello world!"), isCompletelyDisplayed());
onView(withText("Focus element")).perform(click());
waitUntilViewMatchesCondition(withText("RESIZE_VISUAL_VIEWPORT"), isCompletelyDisplayed());
// The viewport should be resized so that the bottom element is not covered by the bottom
// sheet.
checkElementIsCoveredByBottomsheetByAtMost("bottom", 10);
onView(withId(R.id.swipe_indicator)).perform(swipeDownToMinimize());
checkElementIsCoveredByBottomsheetByAtMost("bottom", 10);
onView(withText("RESIZE_VISUAL_VIEWPORT")).check(matches(not(isDisplayed())));
onView(withId(R.id.swipe_indicator)).perform(swipeUpToExpand());
checkElementIsCoveredByBottomsheet("bottom");
onView(withText("RESIZE_VISUAL_VIEWPORT")).check(matches(isCompletelyDisplayed()));
}
private ViewAction swipeDownToMinimize() {
return actionWithAssertions(
new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER, view -> {
float[] coordinates = GeneralLocation.CENTER.calculateCoordinates(view);
coordinates[1] =
view.getContext().getResources().getDisplayMetrics().heightPixels;
return coordinates;
}, Press.FINGER));
}
private ViewAction swipeUpToExpand() {
return actionWithAssertions(
new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER, view -> {
float[] coordinates = GeneralLocation.CENTER.calculateCoordinates(view);
coordinates[1] = 0;
return coordinates;
}, Press.FINGER));
}
private void checkElementIsCoveredByBottomsheet(String elementId) {
validateElementsCoverageByBottomsheet(elementId,
(Integer percCovered)
-> percCovered > 0,
"Time out while waiting for element '" + elementId
+ "' to become covered by bottomsheet.");
}
private void checkElementIsCoveredByBottomsheetByAtMost(String elementId, int maxPercCovered) {
validateElementsCoverageByBottomsheet(elementId,
(Integer percCovered)
-> percCovered <= maxPercCovered,
"Time out while waiting for element '" + elementId
+ "' to become covered by bottomsheet by at most " + maxPercCovered + "%.");
}
// TODO: it would be better to merge this method with waitUntilViewMatchesCondition
/* Check whether the element is covered by the bottomsheet*/
private void validateElementsCoverageByBottomsheet(
String elementId, Function<Integer, Boolean> percValidation, String message) {
CriteriaHelper.pollInstrumentationThread(new Criteria(message) {
@Override
public boolean isSatisfied() {
try {
float y = GeneralLocation.TOP_CENTER.calculateCoordinates(
mTestRule.getActivity().findViewById(R.id.bottom_sheet))[1];
Rect el = getAbsoluteBoundingRect(elementId, mTestRule);
int percCovered = (int) ((el.bottom - y) / (el.bottom - el.top) * 100);
return percValidation.apply(percCovered);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
}
......@@ -13,6 +13,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementExists;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getAbsoluteBoundingRect;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getBoundingRectForElement;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getViewport;
import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
import android.graphics.Bitmap;
......@@ -24,7 +28,6 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.util.DisplayMetrics;
import org.json.JSONArray;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
......@@ -92,7 +95,7 @@ public class AutofillAssistantOverlayUiTest {
assertScrimDisplayed(false);
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(false));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(false));
}
/** Tests assumptions about the full overlay. */
......@@ -106,13 +109,13 @@ public class AutofillAssistantOverlayUiTest {
() -> model.set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL));
assertScrimDisplayed(true);
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(true));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(true));
runOnUiThreadBlocking(
() -> model.set(AssistantOverlayModel.STATE, AssistantOverlayState.HIDDEN));
assertScrimDisplayed(false);
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(false));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(false));
}
/** Tests assumptions about the full overlay. */
......@@ -146,30 +149,30 @@ public class AutofillAssistantOverlayUiTest {
() -> model.set(AssistantOverlayModel.STATE, AssistantOverlayState.PARTIAL));
assertScrimDisplayed(true);
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(true));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(true));
Rect rect = getBoundingRectForElement("touch_area_one");
Rect rect = getBoundingRectForElement("touch_area_one", getWebContents());
runOnUiThreadBlocking(()
-> model.set(AssistantOverlayModel.TOUCHABLE_AREA,
Collections.singletonList(new RectF(rect))));
// Touchable area set, but no viewport given: equivalent to full overlay.
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(true));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(true));
// Set viewport.
Rect viewport = getViewport();
Rect viewport = getViewport(getWebContents());
runOnUiThreadBlocking(
() -> model.set(AssistantOverlayModel.VISUAL_VIEWPORT, new RectF(viewport)));
// Now the partial overlay allows tapping the highlighted touch area.
tapElement("touch_area_one");
assertThat(checkElementExists("touch_area_one"), is(false));
assertThat(checkElementExists("touch_area_one", getWebContents()), is(false));
runOnUiThreadBlocking(
() -> model.set(AssistantOverlayModel.TOUCHABLE_AREA, Collections.emptyList()));
tapElement("touch_area_three");
assertThat(checkElementExists("touch_area_three"), is(true));
assertThat(checkElementExists("touch_area_three", getWebContents()), is(true));
}
/** Scrolls a touchable area into view and then taps it. */
......@@ -179,8 +182,8 @@ public class AutofillAssistantOverlayUiTest {
AssistantOverlayModel model = new AssistantOverlayModel();
AssistantOverlayCoordinator coordinator = createCoordinator(model);
Rect rect = getBoundingRectForElement("touch_area_two");
Rect viewport = getViewport();
Rect rect = getBoundingRectForElement("touch_area_two", getWebContents());
Rect viewport = getViewport(getWebContents());
runOnUiThreadBlocking(() -> {
model.set(AssistantOverlayModel.STATE, AssistantOverlayState.PARTIAL);
model.set(AssistantOverlayModel.TOUCHABLE_AREA,
......@@ -188,11 +191,11 @@ public class AutofillAssistantOverlayUiTest {
model.set(AssistantOverlayModel.VISUAL_VIEWPORT, new RectF(viewport));
});
scrollIntoViewIfNeeded("touch_area_two");
Rect newViewport = getViewport();
Rect newViewport = getViewport(getWebContents());
runOnUiThreadBlocking(
() -> model.set(AssistantOverlayModel.VISUAL_VIEWPORT, new RectF(newViewport)));
tapElement("touch_area_two");
assertThat(checkElementExists("touch_area_two"), is(false));
assertThat(checkElementExists("touch_area_two", getWebContents()), is(false));
}
private void assertScrimDisplayed(boolean expected) throws Exception {
......@@ -217,7 +220,7 @@ public class AutofillAssistantOverlayUiTest {
/** Performs a single tap on the center of the specified element. */
private void tapElement(String elementId) throws Exception {
Rect coords = getAbsoluteBoundingRect(elementId);
Rect coords = getAbsoluteBoundingRect(elementId, mTestRule);
float x = coords.left + 0.5f * (coords.right - coords.left);
float y = coords.top + 0.5f * (coords.bottom - coords.top);
......@@ -231,82 +234,6 @@ public class AutofillAssistantOverlayUiTest {
TestTouchUtils.singleClick(InstrumentationRegistry.getInstrumentation(), x, y);
}
/** Computes the bounding rectangle of the specified DOM element in absolute screen space. */
private Rect getAbsoluteBoundingRect(String elementId) throws Exception {
// Get bounding rectangle in viewport space.
Rect elementRect = getBoundingRectForElement(elementId);
/*
* Conversion from viewport space to screen space is done in two steps:
* - First, convert viewport to compositor space (scrolling offset, multiply with factor).
* - Then, convert compositor space to screen space (add content offset).
*/
Rect viewport = getViewport();
float cssToPysicalPixels =
(((float) mTestRule.getActivity().getCompositorViewHolder().getWidth()
/ (float) viewport.width()));
int[] compositorLocation = new int[2];
mTestRule.getActivity().getCompositorViewHolder().getLocationOnScreen(compositorLocation);
int offsetY = compositorLocation[1]
+ mTestRule.getActivity().getFullscreenManager().getContentOffset();
return new Rect((int) ((elementRect.left - viewport.left) * cssToPysicalPixels),
(int) ((elementRect.top - viewport.top) * cssToPysicalPixels + offsetY),
(int) ((elementRect.right - viewport.left) * cssToPysicalPixels),
(int) ((elementRect.bottom - viewport.top) * cssToPysicalPixels + offsetY));
}
/**
* Retrieves the bounding rectangle for the specified element in the DOM tree in CSS pixel
* coordinates.
*/
private Rect getBoundingRectForElement(String elementId) throws Exception {
if (!checkElementExists(elementId)) {
throw new IllegalArgumentException(elementId + " does not exist");
}
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(getWebContents(),
"(function() {"
+ " rect = document.getElementById('" + elementId
+ "').getBoundingClientRect();"
+ " return [window.scrollX + rect.left, window.scrollY + rect.top, "
+ " window.scrollX + rect.right, window.scrollY + rect.bottom];"
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray rectJson = new JSONArray(javascriptHelper.getJsonResultAndClear());
return new Rect(
rectJson.getInt(0), rectJson.getInt(1), rectJson.getInt(2), rectJson.getInt(3));
}
/** Checks whether the specified element exists in the DOM tree. */
private boolean checkElementExists(String elementId) throws Exception {
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(getWebContents(),
"(function() {"
+ " return [document.getElementById('" + elementId + "') != null]; "
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray result = new JSONArray(javascriptHelper.getJsonResultAndClear());
return result.getBoolean(0);
}
/**
* Retrieves the visual viewport of the webpage in CSS pixel coordinates.
*/
private Rect getViewport() throws Exception {
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(getWebContents(),
"(function() {"
+ " const v = window.visualViewport;"
+ " return [v.pageLeft, v.pageTop, v.width, v.height]"
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray values = new JSONArray(javascriptHelper.getJsonResultAndClear());
return new Rect(values.getInt(0), values.getInt(1), values.getInt(2), values.getInt(3));
}
/**
* Scrolls to the specified element on the webpage, if necessary.
......
......@@ -8,6 +8,7 @@ import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.support.design.widget.CoordinatorLayout;
import android.support.test.InstrumentationRegistry;
......@@ -28,6 +29,7 @@ import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
import org.json.JSONArray;
import org.chromium.base.Callback;
import org.chromium.base.Supplier;
......@@ -41,8 +43,10 @@ import org.chromium.chrome.browser.image_fetcher.ImageFetcher;
import org.chromium.chrome.browser.image_fetcher.ImageFetcherConfig;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestCallbackHelperContainer;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.util.ArrayList;
......@@ -255,4 +259,84 @@ class AutofillAssistantUiTestUtil {
testService.scheduleForInjection();
TestThreadUtils.runOnUiThreadBlocking(() -> AutofillAssistantFacade.start(activity));
}
/** Computes the bounding rectangle of the specified DOM element in absolute screen space. */
public static Rect getAbsoluteBoundingRect(String elementId, CustomTabActivityTestRule testRule)
throws Exception {
// Get bounding rectangle in viewport space.
Rect elementRect = getBoundingRectForElement(elementId, testRule.getWebContents());
/*
* Conversion from viewport space to screen space is done in two steps:
* - First, convert viewport to compositor space (scrolling offset, multiply with factor).
* - Then, convert compositor space to screen space (add content offset).
*/
Rect viewport = getViewport(testRule.getWebContents());
float cssToPysicalPixels =
(((float) testRule.getActivity().getCompositorViewHolder().getWidth()
/ (float) viewport.width()));
int[] compositorLocation = new int[2];
testRule.getActivity().getCompositorViewHolder().getLocationOnScreen(compositorLocation);
int offsetY = compositorLocation[1]
+ testRule.getActivity().getFullscreenManager().getContentOffset();
return new Rect((int) ((elementRect.left - viewport.left) * cssToPysicalPixels),
(int) ((elementRect.top - viewport.top) * cssToPysicalPixels + offsetY),
(int) ((elementRect.right - viewport.left) * cssToPysicalPixels),
(int) ((elementRect.bottom - viewport.top) * cssToPysicalPixels + offsetY));
}
/**
* Retrieves the bounding rectangle for the specified element in the DOM tree in CSS pixel
* coordinates.
*/
public static Rect getBoundingRectForElement(String elementId, WebContents webContents)
throws Exception {
if (!checkElementExists(elementId, webContents)) {
throw new IllegalArgumentException(elementId + " does not exist");
}
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(webContents,
"(function() {"
+ " rect = document.getElementById('" + elementId
+ "').getBoundingClientRect();"
+ " return [window.scrollX + rect.left, window.scrollY + rect.top, "
+ " window.scrollX + rect.right, window.scrollY + rect.bottom];"
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray rectJson = new JSONArray(javascriptHelper.getJsonResultAndClear());
return new Rect(
rectJson.getInt(0), rectJson.getInt(1), rectJson.getInt(2), rectJson.getInt(3));
}
/** Checks whether the specified element exists in the DOM tree. */
public static boolean checkElementExists(String elementId, WebContents webContents)
throws Exception {
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(webContents,
"(function() {"
+ " return [document.getElementById('" + elementId + "') != null]; "
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray result = new JSONArray(javascriptHelper.getJsonResultAndClear());
return result.getBoolean(0);
}
/**
* Retrieves the visual viewport of the webpage in CSS pixel coordinates.
*/
public static Rect getViewport(WebContents webContents) throws Exception {
TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper javascriptHelper =
new TestCallbackHelperContainer.OnEvaluateJavaScriptResultHelper();
javascriptHelper.evaluateJavaScriptForTests(webContents,
"(function() {"
+ " const v = window.visualViewport;"
+ " return [v.pageLeft, v.pageTop, v.width, v.height]"
+ "})()");
javascriptHelper.waitUntilHasValue();
JSONArray values = new JSONArray(javascriptHelper.getJsonResultAndClear());
return new Rect(values.getInt(0), values.getInt(1), values.getInt(2), values.getInt(3));
}
}
<!DOCTYPE html>
<!--
Copyright 2019 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.
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bottomsheet Behaviour Test</title>
<style>
body {
margin: 0;
padding: 0;
}
nav {
width: 100%;
position: fixed;
height: 2em;
background-color: lightgreen;
}
p {
height: 5em;
}
.top {
background-color: lightblue;
}
.bottom {
background-color: yellow;
}
.cast {
height: 100px;
margin: 20px;
border: 2px solid black;
border-radius: 15px;
}
.blank {
height: 100em;
}
</style>
</head>
<body>
<nav>Navbar</nav>
<p class="top">Top text hidden</p>
<div class="cast"></div>
<div class="blank">Blank</div>
<p id="bottom" class="bottom">Bottom text</p>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment