blob: a9b4db2363d145709f97a9e1142bbe80652ce454 [file] [log] [blame]
// Copyright 2020 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 androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.chromium.base.test.util.CriteriaHelper.DEFAULT_MAX_TIME_TO_POLL;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementExists;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.checkElementOnScreen;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.getBitmapFromView;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.tapElement;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntil;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewAssertionTrue;
import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
import static org.chromium.chrome.browser.autofill_assistant.ProtoTestUtil.toCssSelector;
import static org.chromium.chrome.browser.autofill_assistant.ProtoTestUtil.toIFrameCssSelector;
import android.os.Build.VERSION_CODES;
import android.view.View;
import android.view.ViewGroup;
import androidx.test.filters.MediumTest;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.BitmapDrawableProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ClientDimensionProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ClientSettingsProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureUiStateProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ConfigureUiStateProto.OverlayBehavior;
import org.chromium.chrome.browser.autofill_assistant.proto.DrawableProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ElementAreaProto.Rectangle;
import org.chromium.chrome.browser.autofill_assistant.proto.OverlayImageProto;
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.SelectorProto;
import org.chromium.chrome.browser.autofill_assistant.proto.ShowCastProto;
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.flags.ChromeSwitches;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.autofill_assistant.R;
import org.chromium.ui.test.util.UiRestriction;
import java.util.ArrayList;
import java.util.Collections;
/**
* Tests autofill assistant's overlay.
*/
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@RunWith(ChromeJUnit4ClassRunner.class)
public class AutofillAssistantOverlayIntegrationTest {
private final CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
@Rule
public final TestRule mRulesChain =
RuleChain.outerRule(mTestRule).around(new AutofillAssistantCustomTabTestRule(
mTestRule, "autofill_assistant_target_website.html"));
/**
* Tests that clicking on a document element works with a showcast.
*/
@Test
@MediumTest
@Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
public void testShowCastOnDocumentElement() throws Exception {
SelectorProto element = toCssSelector("#touch_area_one");
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices(
PromptProto.Choice.newBuilder()))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "touch_area_one"));
waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
// Tapping on the element should remove it from the DOM.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_one"), is(true));
tapElement(mTestRule, "touch_area_one");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_one"));
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "touch_area_four");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
}
/**
* Showcasts the same element twice in a row, and taps it the second time. Tests that the second
* showcast works correctly, even though the showcasted area hasn't changed (see b/161471176).
*/
@Test
@MediumTest
public void testRepeatedShowCastOnSameElement() throws Exception {
SelectorProto element = toCssSelector("#touch_area_one");
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("First Prompt")
.addChoices(PromptProto.Choice.newBuilder().setChip(
ChipProto.newBuilder().setText("Continue"))))
.build());
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("Second Prompt")
.addChoices(PromptProto.Choice.newBuilder().setChip(
ChipProto.newBuilder().setText("Done"))))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "touch_area_one"));
waitUntilViewMatchesCondition(withText("First Prompt"), isCompletelyDisplayed());
onView(withContentDescription("Continue")).perform(click());
waitUntilViewMatchesCondition(withText("Second Prompt"), isCompletelyDisplayed());
// Tapping on the element should remove it from the DOM.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_one"), is(true));
tapElement(mTestRule, "touch_area_one");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_one"));
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "touch_area_four");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
}
/**
* Tests that clicking on a document element requiring scrolling works with a showcast.
*/
@Test
@MediumTest
public void testShowCastOnDocumentElementInScrolledBrowserWindow() throws Exception {
SelectorProto element = toCssSelector("#touch_area_five");
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices(
PromptProto.Choice.newBuilder()))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "touch_area_five"));
waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
// Tapping on the element should remove it from the DOM. The element should be after a
// big element forcing the page to scroll.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_five"), is(true));
tapElement(mTestRule, "touch_area_five");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_five"));
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "touch_area_six");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_six"), is(true));
}
/**
* Tests that clicking on an iFrame element works with a showcast.
*/
@Test
@MediumTest
@DisabledTest(message = "https://crbug.com/1272997")
public void testShowCastOnIFrameElement() throws Exception {
SelectorProto element = toIFrameCssSelector("#iframe", "#touch_area_1");
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices(
PromptProto.Choice.newBuilder()))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "iframe", "touch_area_1"));
waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
// Tapping on the element should remove it from the DOM.
assertThat(
checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_1"), is(true));
tapElement(mTestRule, "iframe", "touch_area_1");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_1"));
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "iframe", "touch_area_2");
assertThat(
checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_2"), is(true));
}
/**
* Tests that clicking on an iFrame element works with a showcast in a scrolled iFrame.
*/
@Test
@MediumTest
@DisableIf.Build(sdk_is_greater_than = VERSION_CODES.Q,
message = "Flaky on Android 11: https://crbug.com/1276995")
public void
testShowCastOnIFrameElementInScrollIFrame() throws Exception {
SelectorProto element = toIFrameCssSelector("#iframe", "#touch_area_3");
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setShowCast(ShowCastProto.newBuilder()
.setElementToPresent(element)
.setTouchableElementArea(
ElementAreaProto.newBuilder().addTouchable(
Rectangle.newBuilder().addElements(
element))))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder().setMessage("Prompt").addChoices(
PromptProto.Choice.newBuilder()))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "iframe", "touch_area_3"));
waitUntilViewMatchesCondition(withText("Prompt"), isCompletelyDisplayed());
// Tapping on the element should remove it from the DOM. The element should be after a
// big element forcing the page to scroll.
assertThat(
checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_3"), is(true));
tapElement(mTestRule, "iframe", "touch_area_3");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_3"));
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "iframe", "touch_area_4");
assertThat(
checkElementExists(mTestRule.getWebContents(), "iframe", "touch_area_4"), is(true));
}
/**
* Tests that three taps on the scrim removes the scrim and hides the Autofill Assistant.
*/
@Test
@MediumTest
@DisabledTest(message = "Flaky - https://crbug.com/1269961")
public void testThreeClicksHideAssistant() throws Exception {
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setPrompt(
PromptProto.newBuilder()
.setMessage("Overlay present")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(org.chromium.chrome.browser
.autofill_assistant.proto
.ChipType.DONE_ACTION)
.setText("Hide"))))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "touch_area_one"));
waitUntilViewMatchesCondition(withText("Overlay present"), isCompletelyDisplayed());
// Tapping the scrim three times should hide the scrim and the Autofill Assistant.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
tapElement(mTestRule, "touch_area_four");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
tapElement(mTestRule, "touch_area_four");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
tapElement(mTestRule, "touch_area_four");
// Afterwards the scrim and Autofill Assistant are removed.
waitUntil(()
-> mTestRule.getActivity()
.getRootUiCoordinatorForTesting()
.getScrimCoordinator()
.getViewForTesting()
== null);
waitUntilViewAssertionTrue(
withId(R.id.autofill_assistant), doesNotExist(), DEFAULT_MAX_TIME_TO_POLL);
}
/**
* Tests that changing the OverlayBehavior setting affects the overlay as intended.
*/
@Test
@MediumTest
public void testOverlayBehaviorSetting() throws Exception {
ArrayList<ActionProto> list = new ArrayList<>();
list.add(ActionProto.newBuilder()
.setPrompt(
PromptProto.newBuilder()
.setMessage("Overlay present")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(org.chromium.chrome.browser
.autofill_assistant.proto
.ChipType.DONE_ACTION)
.setText("Hide"))))
.build());
list.add(ActionProto.newBuilder()
.setConfigureUiState(ConfigureUiStateProto.newBuilder().setOverlayBehavior(
OverlayBehavior.HIDDEN))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(
PromptProto.newBuilder()
.setMessage("Overlay hidden")
.addChoices(Choice.newBuilder().setChip(
ChipProto.newBuilder()
.setType(org.chromium.chrome.browser
.autofill_assistant.proto
.ChipType.DONE_ACTION)
.setText("Default"))))
.build());
list.add(ActionProto.newBuilder()
.setConfigureUiState(ConfigureUiStateProto.newBuilder().setOverlayBehavior(
OverlayBehavior.DEFAULT))
.build());
list.add(ActionProto.newBuilder()
.setPrompt(PromptProto.newBuilder()
.setMessage("Overlay present")
.addChoices(PromptProto.Choice.newBuilder()))
.build());
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
list);
runScript(script);
waitUntil(() -> checkElementOnScreen(mTestRule, "touch_area_one"));
waitUntilViewMatchesCondition(withText("Overlay present"), isCompletelyDisplayed());
// Tapping the element should not do anything since the overlay should be present.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_one"), is(true));
tapElement(mTestRule, "touch_area_one");
// Go to the next action to hide the overlay.
onView(withText("Hide")).perform(click());
// Tapping the item should now hide it.
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_one"), is(true));
tapElement(mTestRule, "touch_area_one");
waitUntil(() -> !checkElementExists(mTestRule.getWebContents(), "touch_area_one"));
// Go to the next action to set the overlay to full again.
onView(withText("Default")).perform(click());
// Tapping on the element should be blocked by the overlay.
tapElement(mTestRule, "touch_area_four");
assertThat(checkElementExists(mTestRule.getWebContents(), "touch_area_four"), is(true));
}
/**
* Tests that an image works with an overlay
*/
@Test
@MediumTest
public void testShowImageOnOverlay() throws Exception {
String redDotBase64Url =
"data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
int imageSizeInPixel = 50;
ClientSettingsProto clientSettings =
ClientSettingsProto.newBuilder()
.setOverlayImage(
OverlayImageProto.newBuilder()
.setImageDrawable(DrawableProto.newBuilder().setBitmap(
BitmapDrawableProto.newBuilder()
.setUrl(redDotBase64Url)
.setWidth(ClientDimensionProto.newBuilder()
.setSizeInPixel(
imageSizeInPixel))
.setHeight(
ClientDimensionProto.newBuilder()
.setSizeInPixel(
imageSizeInPixel))))
.setImageSize(
ClientDimensionProto.newBuilder().setSizeInPixel(
imageSizeInPixel)))
.build();
AutofillAssistantTestScript script = new AutofillAssistantTestScript(
SupportedScriptProto.newBuilder()
.setPath("autofill_assistant_target_website.html")
.setPresentation(PresentationProto.newBuilder().setAutostart(true))
.build(),
new ArrayList<>());
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script), clientSettings);
startAutofillAssistant(mTestRule.getActivity(), testService);
ViewGroup chromeCoordinatorView = mTestRule.getActivity().findViewById(R.id.coordinator);
View scrim = mTestRule.getActivity()
.getRootUiCoordinatorForTesting()
.getScrimCoordinator()
.getViewForTesting();
onView(is(scrim)).check(matches(isCompletelyDisplayed()));
int image_center_x = scrim.getWidth() / 2;
int yTopContentOffset = mTestRule.getActivity()
.getRootUiCoordinatorForTesting()
.getBrowserControlsManager()
.getContentOffset();
int image_center_y = yTopContentOffset + imageSizeInPixel / 2;
// Testing that central pixel of overlay image is different from (0,0) pixel
waitUntil(()
-> getBitmapFromView(scrim).getPixel(image_center_x, image_center_y)
!= getBitmapFromView(scrim).getPixel(0, 0));
}
private void runScript(AutofillAssistantTestScript script) {
AutofillAssistantTestService testService =
new AutofillAssistantTestService(Collections.singletonList(script));
startAutofillAssistant(mTestRule.getActivity(), testService);
}
}