Commit bc520eee authored by gogerald's avatar gogerald Committed by Commit Bot

[Autofill Assistant] Add initial UI tests

This CL depends on https://chromium-review.googlesource.com/c/chromium/src/+/1366696

Bug: 806868
Change-Id: I9b7093418ebf7d270a333deca572deea59166681
Reviewed-on: https://chromium-review.googlesource.com/c/1359552
Commit-Queue: Ganggui Tang <gogerald@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Reviewed-by: default avatarStephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#615622}
parent 69017ce4
// Copyright 2018 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;
/**
* Abstract AutofillAssistantUiController, this is created for testing UI.
*
* TODO(crbug.com/806868): Refactor this class to AutofillAssistantUiController interface and the
* original AutofillAssistantUiController to AutofillAssistantUiControllerImpl after done necessary
* merges to M72.
*/
abstract class AbstractAutofillAssistantUiController
implements AutofillAssistantUiDelegate.Client, UiDelegateHolder.Listener {
/** Interface called to initiate controller. */
public void init(UiDelegateHolder delegateHolder, Details details) {}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.autofill_assistant;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
......@@ -36,18 +37,28 @@ public class AutofillAssistantFacade {
/** Starts Autofill Assistant on the given {@code activity}. */
public static void start(ChromeActivity activity) {
startInternal(activity, /* controller= */ null);
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
static void startInternal(
ChromeActivity activity, @Nullable AbstractAutofillAssistantUiController controller) {
Map<String, String> parameters = extractParameters(activity.getInitialIntent().getExtras());
parameters.remove(PARAMETER_ENABLED);
final AbstractAutofillAssistantUiController targetController = controller == null
? new AutofillAssistantUiController(activity, parameters)
: controller;
if (!AutofillAssistantPreferencesUtil.getSkipInitScreenPreference()) {
FirstRunScreen.show(activity, (result) -> {
if (result) initiateAutofillAssistant(activity, parameters);
if (result) initiateAutofillAssistant(activity, parameters, targetController);
});
return;
}
if (AutofillAssistantPreferencesUtil.isAutofillAssistantSwitchOn()
&& AutofillAssistantPreferencesUtil.getSkipInitScreenPreference()) {
initiateAutofillAssistant(activity, parameters);
initiateAutofillAssistant(activity, parameters, targetController);
}
// We don't have consent to start Autofill Assistant and cannot show initial screen.
// Do nothing.
......@@ -56,12 +67,10 @@ public class AutofillAssistantFacade {
/**
* Instantiates all essential Autofill Assistant components and starts it.
*/
private static void initiateAutofillAssistant(
ChromeActivity activity, Map<String, String> parameters) {
AutofillAssistantUiController controller =
new AutofillAssistantUiController(activity, parameters);
private static void initiateAutofillAssistant(ChromeActivity activity,
Map<String, String> parameters, AbstractAutofillAssistantUiController controller) {
UiDelegateHolder delegateHolder = new UiDelegateHolder(
controller, new AutofillAssistantUiDelegate(activity, controller));
new AutofillAssistantUiDelegate(activity, controller), controller);
initTabObservers(activity, delegateHolder);
controller.init(delegateHolder, Details.makeFromParameters(parameters));
......
......@@ -34,7 +34,7 @@ import java.util.Map;
* Autofill Assistant related UIs and forward UI events to native side.
*/
@JNINamespace("autofill_assistant")
public class AutofillAssistantUiController implements AutofillAssistantUiDelegate.Client {
public class AutofillAssistantUiController extends AbstractAutofillAssistantUiController {
/** OAuth2 scope that RPCs require. */
private static final String AUTH_TOKEN_TYPE =
"oauth2:https://www.googleapis.com/auth/userinfo.profile";
......@@ -94,6 +94,7 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
mUiControllerAndroid = 0;
}
@Override
public void init(UiDelegateHolder delegateHolder, Details details) {
mUiDelegateHolder = delegateHolder;
maybeUpdateDetails(details);
......@@ -163,17 +164,8 @@ public class AutofillAssistantUiController implements AutofillAssistantUiDelegat
return nativeOnRequestDebugContext(mUiControllerAndroid);
}
/**
* Immediately and unconditionally destroys the UI Controller.
*
* <p>Call {@link UiDelegateHolder#shutdown} to shutdown Autofill Assistant properly and safely.
*
* <p>Destroy is different from shutdown in that {@code unsafeDestroy} just deletes the native
* controller and all the objects it owns, without changing the state of the UI. When that
* happens, everything stops irrevocably on the native side. Doing this at the wrong time will
* cause crashes.
*/
void unsafeDestroy() {
@Override
public void onCompleteShutdown() {
if (mUiControllerAndroid != 0) nativeDestroy(mUiControllerAndroid);
}
......
......@@ -19,8 +19,8 @@ class UiDelegateHolder {
/** Display the final message for that long before shutting everything down. */
private static final long GRACEFUL_SHUTDOWN_DELAY_MS = 5_000;
private final AutofillAssistantUiController mUiController;
private final AutofillAssistantUiDelegate mUiDelegate;
private final Listener mListener;
private boolean mPaused;
private boolean mHasBeenShutdown;
......@@ -29,10 +29,14 @@ class UiDelegateHolder {
private final Queue<Callback<AutofillAssistantUiDelegate>> mPendingUiOperations =
new ArrayDeque<>();
UiDelegateHolder(
AutofillAssistantUiController controller, AutofillAssistantUiDelegate uiDelegate) {
mUiController = controller;
public interface Listener {
/** Called when UI completes change for shutdown. */
public void onCompleteShutdown();
}
UiDelegateHolder(AutofillAssistantUiDelegate uiDelegate, Listener listener) {
mUiDelegate = uiDelegate;
mListener = listener;
}
/**
......@@ -126,7 +130,7 @@ class UiDelegateHolder {
mUiDelegate.dismissSnackbar(mDismissSnackbar);
}
mUiDelegate.hide();
mUiController.unsafeDestroy();
mListener.onCompleteShutdown();
}
/**
......
......@@ -125,6 +125,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetCoordinator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetMediator.java",
"java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AbstractAutofillAssistantUiController.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AnimatedProgressBar.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFacade.java",
"java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantPaymentRequest.java",
......@@ -1880,6 +1881,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessoryIntegrationTest.java",
"javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewTest.java",
"javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java",
"javatests/src/org/chromium/chrome/browser/banners/AppBannerManagerTest.java",
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkBridgeTest.java",
"javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java",
......
// Copyright 2018 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 org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
import org.chromium.base.PathUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.test.util.UrlUtils;
import org.chromium.chrome.autofill_assistant.R;
import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
import org.chromium.chrome.browser.firstrun.FirstRunStatus;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.net.test.EmbeddedTestServer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
/**
* Instrumentation tests for autofill assistant UI.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
public class AutofillAssistantUiTest {
private String mTestPage;
private EmbeddedTestServer mTestServer;
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private AbstractAutofillAssistantUiController mControllerMock;
@Captor
private ArgumentCaptor<UiDelegateHolder> mUiDelegateHolderCaptor;
@Captor
private ArgumentCaptor<String> mLastSelectedScriptPathCaptor;
@Rule
public CustomTabActivityTestRule mCustomTabActivityTestRule = new CustomTabActivityTestRule();
@Before
public void setUp() throws Exception {
ThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true));
mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext());
mTestPage = mTestServer.getURL(UrlUtils.getIsolatedTestFilePath(
"components/test/data/autofill_assistant/autofill_assistant_target_website.html"));
PathUtils.setPrivateDataDirectorySuffix("chrome");
LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
}
@After
public void tearDown() throws Exception {
ThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(false));
mTestServer.stopAndDestroyServer();
}
/**
* @see CustomTabsTestUtils#createMinimalCustomTabIntent(Context, String).
*/
private Intent createMinimalCustomTabIntent() {
return CustomTabsTestUtils.createMinimalCustomTabIntent(
InstrumentationRegistry.getTargetContext(), mTestPage);
}
private View findViewByIdInMainCoordinator(int id) {
return getActivity().findViewById(R.id.coordinator).findViewById(id);
}
private CustomTabActivity getActivity() {
return mCustomTabActivityTestRule.getActivity();
}
// TODO(crbug.com/806868): Add more UI details test and check, like payment request UI,
// highlight chips and so on.
@Test
@MediumTest
public void testStartAndDismiss() throws InterruptedException {
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(createMinimalCustomTabIntent());
// Start autofill assistant UI. The first run screen must be shown first since the
// preference hasn't been set.
ThreadUtils.runOnUiThreadBlocking(
() -> AutofillAssistantFacade.startInternal(getActivity(), mControllerMock));
View firstRunScreen = findViewByIdInMainCoordinator(R.id.init_screen);
Assert.assertNotNull(firstRunScreen);
Assert.assertTrue(firstRunScreen.isShown());
verify(mControllerMock, never()).init(any(), any());
// Accept on the first run screen so as to continue.
View initOkButton = firstRunScreen.findViewById(R.id.button_init_ok);
ThreadUtils.runOnUiThreadBlocking(() -> { initOkButton.performClick(); });
verify(mControllerMock, times(1))
.init(mUiDelegateHolderCaptor.capture(), any(Details.class));
Assert.assertNotNull(mUiDelegateHolderCaptor.getValue());
// Show and check status message.
String testStatusMessage = "test message";
ThreadUtils.runOnUiThreadBlocking(
()
-> mUiDelegateHolderCaptor.getValue().performUiOperation(
uiDelegate -> uiDelegate.showStatusMessage(testStatusMessage)));
View bottomSheet = findViewByIdInMainCoordinator(R.id.autofill_assistant);
Assert.assertTrue(bottomSheet.isShown());
TextView statusMessageView = (TextView) bottomSheet.findViewById(R.id.status_message);
Assert.assertEquals(statusMessageView.getText(), testStatusMessage);
// Show overlay.
ThreadUtils.runOnUiThreadBlocking(
() -> mUiDelegateHolderCaptor.getValue().performUiOperation(uiDelegate -> {
uiDelegate.showOverlay();
uiDelegate.disableProgressBarPulsing();
}));
View overlay = bottomSheet.findViewById(R.id.touch_event_filter);
Assert.assertTrue(overlay.isShown());
// Show scripts.
List<AutofillAssistantUiDelegate.ScriptHandle> scriptHandles = new ArrayList<>();
scriptHandles.add(
new AutofillAssistantUiDelegate.ScriptHandle("testScript1", false, "path1"));
scriptHandles.add(
new AutofillAssistantUiDelegate.ScriptHandle("testScript2", false, "path2"));
ThreadUtils.runOnUiThreadBlocking(
()
-> mUiDelegateHolderCaptor.getValue().performUiOperation(
uiDelegate -> uiDelegate.updateScripts(scriptHandles)));
ViewGroup chipsViewContainer = (ViewGroup) bottomSheet.findViewById(R.id.carousel);
Assert.assertEquals(2, chipsViewContainer.getChildCount());
// choose the first script.
ThreadUtils.runOnUiThreadBlocking(
() -> { chipsViewContainer.getChildAt(0).performClick(); });
verify(mControllerMock, times(1)).onScriptSelected(mLastSelectedScriptPathCaptor.capture());
Assert.assertEquals("path1", mLastSelectedScriptPathCaptor.getValue());
// Show movie details.
String movieTitle = "testTitle";
String movieDescription = "This is a fancy test movie";
ThreadUtils.runOnUiThreadBlocking(
()
-> mUiDelegateHolderCaptor.getValue().performUiOperation(uiDelegate
-> uiDelegate.showDetails(new Details(movieTitle, /* url = */ "",
Calendar.getInstance().getTime(), movieDescription,
/* mId = */ "",
/* isFinal= */ true, Collections.emptySet()))));
TextView detailsTitle = (TextView) bottomSheet.findViewById(R.id.details_title);
TextView detailsText = (TextView) bottomSheet.findViewById(R.id.details_text);
Assert.assertEquals(detailsTitle.getText(), movieTitle);
Assert.assertTrue(detailsText.getText().toString().contains(movieDescription));
// Progress bar must be shown.
Assert.assertTrue(bottomSheet.findViewById(R.id.progress_bar).isShown());
// Click 'X' button to graceful shutdown.
doAnswer((Answer<Void>) invocation -> {
mUiDelegateHolderCaptor.getValue().dismiss(R.string.autofill_assistant_stopped);
return null;
})
.when(mControllerMock)
.onDismiss();
ThreadUtils.runOnUiThreadBlocking(
() -> { bottomSheet.findViewById(R.id.close_button).performClick(); });
Assert.assertFalse(bottomSheet.isShown());
}
}
\ No newline at end of file
file://components/autofill_assistant/OWNERS
\ No newline at end of file
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