chromium/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java

// Copyright 2015 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.keyboard_accessory.sheet_tabs;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.hamcrest.Matchers.containsString;

import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.isTransformed;
import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.scrollToLastElement;
import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.selectTabAtPosition;
import static org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper.whenDisplayed;

import android.os.Looper;

import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.keyboard_accessory.ManualFillingTestHelper;
import org.chromium.chrome.browser.keyboard_accessory.R;
import org.chromium.chrome.browser.keyboard_accessory.button_group_component.KeyboardAccessoryButtonGroupView;
import org.chromium.chrome.browser.password_manager.PasswordStoreBridge;
import org.chromium.chrome.browser.password_manager.PasswordStoreCredential;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.ui.test.util.UiDisableIf;
import org.chromium.url.GURL;

import java.util.concurrent.TimeoutException;

/** Integration tests for password accessory views. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "show-autofill-signatures"})
public class PasswordAccessoryIntegrationTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    private EmbeddedTestServer mTestServer;
    private PasswordStoreBridge mPasswordStoreBridge;
    private final ManualFillingTestHelper mHelper = new ManualFillingTestHelper(mActivityTestRule);

    @After
    public void tearDown() {
        mHelper.clear();
    }

    @Test
    @SmallTest
    public void testPasswordSheetIsAvailable() {
        mHelper.loadTestPage(false);

        CriteriaHelper.pollUiThread(
                () -> {
                    return mHelper.getOrCreatePasswordAccessorySheet() != null;
                },
                " Password Sheet should be bound to accessory sheet.");
    }

    @Test
    @MediumTest
    @DisabledTest(message = "crbug.com/353952171")
    public void testPasswordSheetDisplaysProvidedItems() throws TimeoutException {
        preparePasswordBridge();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mPasswordStoreBridge.insertPasswordCredential(
                            new PasswordStoreCredential(
                                    new GURL(mTestServer.getURL("/")),
                                    "[email protected]",
                                    "SomeHiddenPassword"));
                });
        mActivityTestRule.loadUrl(
                mTestServer.getURL("/chrome/test/data/password/password_form.html"));
        mHelper.focusPasswordField(false);
        mHelper.waitForKeyboardAccessoryToBeShown();
        mHelper.waitForKeyboardToShow();
        whenDisplayed(isAssignableFrom(KeyboardAccessoryButtonGroupView.class))
                .perform(selectTabAtPosition(0));

        // Check that the provided elements are there.
        whenDisplayed(withText("[email protected]"));
        whenDisplayed(withText("SomeHiddenPassword")).check(matches(isTransformed()));
    }

    private void preparePasswordBridge() {
        Looper.prepare();
        mActivityTestRule.startMainActivityOnBlankPage();
        mTestServer = mActivityTestRule.getTestServer();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mPasswordStoreBridge =
                            new PasswordStoreBridge(mActivityTestRule.getProfile(false));
                });
    }

    @Test
    @SmallTest
    public void testPasswordSheetDisplaysOptions() throws TimeoutException {
        mHelper.loadTestPage(false);
        // Marking the origin as denylisted shows only a very minimal accessory.
        mHelper.cacheCredentials(new String[0], new String[0], true);

        // Focus the field to bring up the accessory.
        mHelper.focusPasswordField();
        mHelper.waitForKeyboardAccessoryToBeShown();
        whenDisplayed(isAssignableFrom(KeyboardAccessoryButtonGroupView.class))
                .perform(selectTabAtPosition(0));

        mHelper.waitForKeyboardToDisappear();
        whenDisplayed(withId(R.id.passwords_sheet)).perform(scrollToLastElement());
        onView(withText(containsString("Manage password"))).check(matches(isDisplayed()));
    }

    @Test
    @MediumTest
    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1111770
    @DisableFeatures({ChromeFeatureList.UNIFIED_PASSWORD_MANAGER_LOCAL_PWD_MIGRATION_WARNING})
    public void testFillsPasswordOnTap() throws TimeoutException {
        preparePasswordBridge();
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mPasswordStoreBridge.insertPasswordCredential(
                            new PasswordStoreCredential(
                                    new GURL(mTestServer.getURL("/")),
                                    "[email protected]",
                                    "ShorterPassword"));
                });
        mHelper.loadUrl("/chrome/test/data/password/password_form.html");
        mHelper.focusPasswordField(false);
        mHelper.waitForKeyboardAccessoryToBeShown();
        mHelper.waitForKeyboardToShow();
        whenDisplayed(isAssignableFrom(KeyboardAccessoryButtonGroupView.class))
                .perform(selectTabAtPosition(0));

        // Click the suggestion.
        whenDisplayed(withText("ShorterPassword")).perform(click());
        // The callback should have triggered and set the reference to the selected Item.
        CriteriaHelper.pollInstrumentationThread(
                () -> mHelper.getPasswordText().equals("ShorterPassword"));
    }

    @Test
    @SmallTest
    public void testDisplaysEmptyStateMessageWithoutSavedPasswords() throws TimeoutException {
        mHelper.loadTestPage(false);
        // Mark the origin as denylisted to have a reason to show the accessory in the first place.
        mHelper.cacheCredentials(new String[0], new String[0], true);

        // Focus the field to bring up the accessory.
        mHelper.focusPasswordField();
        mHelper.waitForKeyboardAccessoryToBeShown();

        // Click the tab to show the sheet and hide the keyboard.
        whenDisplayed(isAssignableFrom(KeyboardAccessoryButtonGroupView.class))
                .perform(selectTabAtPosition(0));
        mHelper.waitForKeyboardToDisappear();
        whenDisplayed(withId(R.id.passwords_sheet));
        onView(withText(containsString("No saved passwords"))).check(matches(isDisplayed()));
    }

    @Test
    @SmallTest
    @DisabledTest(message = "https://crbug.com/1503085")
    public void testEnablesUndenylistingToggle() throws TimeoutException, InterruptedException {
        preparePasswordBridge();
        String url = mTestServer.getURL("/chrome/test/data/password/password_form.html");
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mPasswordStoreBridge.blocklistForTesting(url);
                });
        mActivityTestRule.loadUrl(url);
        mHelper.focusPasswordField(false);
        mHelper.waitForKeyboardAccessoryToBeShown();
        mHelper.waitForKeyboardToShow();
        whenDisplayed(isAssignableFrom(KeyboardAccessoryButtonGroupView.class))
                .perform(selectTabAtPosition(0));

        whenDisplayed(withId(R.id.option_toggle_switch)).check(matches(isNotChecked()));
        onView(withId(R.id.option_toggle_subtitle)).check(matches(withText(R.string.text_off)));

        whenDisplayed(withId(R.id.option_toggle_switch)).perform(click());
        onView(withId(R.id.option_toggle_switch)).check(matches(isChecked()));
        onView(withId(R.id.option_toggle_subtitle)).check(matches(withText(R.string.text_on)));
    }
}