chromium/chrome/android/javatests/src/org/chromium/chrome/browser/sync/IdentityErrorCardPreferenceTest.java

// Copyright 2024 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.sync;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.withId;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import android.view.View;

import androidx.test.filters.LargeTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.HistogramWatcher;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridge;
import org.chromium.chrome.browser.password_manager.PasswordManagerUtilBridgeJni;
import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
import org.chromium.chrome.browser.sync.settings.ManageSyncSettings;
import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.ChromeRenderTestRule;
import org.chromium.chrome.test.util.browser.signin.SigninTestRule;
import org.chromium.components.signin.base.GoogleServiceAuthError;
import org.chromium.ui.test.util.ViewUtils;

/** Test suite for IdentityErrorCardPreference */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
public class IdentityErrorCardPreferenceTest {
    public final SettingsActivityTestRule<ManageSyncSettings> mSettingsActivityTestRule =
            new SettingsActivityTestRule<>(ManageSyncSettings.class);

    public final ChromeTabbedActivityTestRule mActivityTestRule =
            new ChromeTabbedActivityTestRule();

    // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish
    // CTA won't work.
    @Rule
    public final RuleChain mRuleChain =
            RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule);

    @Rule public final SigninTestRule mSigninTestRule = new SigninTestRule();

    private static final String RENDER_TEST_DESCRIPTION = "Identity error card.";
    private static final int RENDER_TEST_REVISION = 1;

    @Rule
    public final ChromeRenderTestRule mRenderTestRule =
            ChromeRenderTestRule.Builder.withPublicCorpus()
                    .setRevision(RENDER_TEST_REVISION)
                    .setDescription(RENDER_TEST_DESCRIPTION)
                    .setBugComponent(ChromeRenderTestRule.Component.SERVICES_SYNC)
                    .build();

    @Rule public JniMocker mJniMocker = new JniMocker();

    @Mock private PasswordManagerUtilBridge.Natives mPasswordManagerUtilBridgeJniMock;

    private FakeSyncServiceImpl mFakeSyncServiceImpl;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mJniMocker.mock(PasswordManagerUtilBridgeJni.TEST_HOOKS, mPasswordManagerUtilBridgeJniMock);

        mActivityTestRule.startMainActivityOnBlankPage();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mFakeSyncServiceImpl = new FakeSyncServiceImpl();
                    SyncServiceFactory.setInstanceForTesting(mFakeSyncServiceImpl);
                });
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForAuthError() throws Exception {
        mFakeSyncServiceImpl.setAuthError(GoogleServiceAuthError.State.INVALID_GAIA_CREDENTIALS);
        mSigninTestRule.addTestAccountThenSignin();

        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.AuthError",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(getIdentityErrorCardView(), "identity_error_card_auth_error");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForClientOutOfDate() throws Exception {
        mFakeSyncServiceImpl.setRequiresClientUpgrade(true);
        mSigninTestRule.addTestAccountThenSignin();

        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.ClientOutOfDate",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(), "identity_error_card_client_out_of_date");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForPassphraseRequired() throws Exception {
        mFakeSyncServiceImpl.setEngineInitialized(true);
        mFakeSyncServiceImpl.setPassphraseRequiredForPreferredDataTypes(true);
        mSigninTestRule.addTestAccountThenSignin();

        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.PassphraseRequired",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(), "identity_error_card_passphrase_required");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForTrustedVaultKey() throws Exception {
        mFakeSyncServiceImpl.setEngineInitialized(true);
        mFakeSyncServiceImpl.setTrustedVaultKeyRequiredForPreferredDataTypes(true);
        mFakeSyncServiceImpl.setEncryptEverythingEnabled(true);
        mSigninTestRule.addTestAccountThenSignin();

        mSettingsActivityTestRule.startSettingsActivity();
        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.TrustedVaultKeyRequiredForEverything",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(), "identity_error_card_trusted_vault_key_required");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForTrustedVaultKeyForPasswords() throws Exception {
        mFakeSyncServiceImpl.setEngineInitialized(true);
        mFakeSyncServiceImpl.setTrustedVaultKeyRequiredForPreferredDataTypes(true);
        mFakeSyncServiceImpl.setEncryptEverythingEnabled(false);
        mSigninTestRule.addTestAccountThenSignin();

        mSettingsActivityTestRule.startSettingsActivity();
        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.TrustedVaultKeyRequiredForPasswords",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(),
                "identity_error_card_trusted_vault_key_required_for_passwords");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForTrustedVaultRecoverabilityDegradedForEverything()
            throws Exception {
        mFakeSyncServiceImpl.setEngineInitialized(true);
        mFakeSyncServiceImpl.setTrustedVaultRecoverabilityDegraded(true);
        mFakeSyncServiceImpl.setEncryptEverythingEnabled(true);
        mSigninTestRule.addTestAccountThenSignin();

        mSettingsActivityTestRule.startSettingsActivity();
        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.TrustedVaultRecoverabilityDegradedForEverything",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(),
                "identity_error_card_trusted_vault_recoverability_degraded_for_everything");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    public void testIdentityErrorCardForTrustedVaultRecoverabilityDegradedForPasswords()
            throws Exception {
        mFakeSyncServiceImpl.setEngineInitialized(true);
        mFakeSyncServiceImpl.setTrustedVaultRecoverabilityDegraded(true);
        mFakeSyncServiceImpl.setEncryptEverythingEnabled(false);
        mSigninTestRule.addTestAccountThenSignin();

        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.TrustedVaultRecoverabilityDegradedForPasswords",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(),
                "identity_error_card_trusted_vault_recoverability_degraded_for_passwords");
    }

    @Test
    @LargeTest
    @Feature("RenderTest")
    @EnableFeatures({ChromeFeatureList.ENABLE_PASSWORDS_ACCOUNT_STORAGE_FOR_NON_SYNCING_USERS})
    public void testIdentityErrorCardForUpmBackendOutdated() throws Exception {
        when(mPasswordManagerUtilBridgeJniMock.isGmsCoreUpdateRequired(any(), any()))
                .thenReturn(true);

        mSigninTestRule.addTestAccountThenSignin();

        try (HistogramWatcher watchIdentityErrorCardShownHistogram =
                HistogramWatcher.newSingleRecordWatcher(
                        "Sync.IdentityErrorCard.UpmBackendOutdated",
                        SyncSettingsUtils.ErrorUiAction.SHOWN)) {
            mSettingsActivityTestRule.startSettingsActivity();
        }
        mRenderTestRule.render(
                getIdentityErrorCardView(), "identity_error_card_upm_backend_outdated");
    }

    @Test
    @LargeTest
    public void testIdentityErrorCardNotShownForUnrecoverableErrors() throws Exception {
        mFakeSyncServiceImpl.setAuthError(GoogleServiceAuthError.State.CONNECTION_FAILED);
        mSigninTestRule.addTestAccountThenSignin();

        mSettingsActivityTestRule.startSettingsActivity();
        onView(withId(R.id.signin_settings_card)).check(doesNotExist());
    }

    private View getIdentityErrorCardView() {
        ViewUtils.waitForVisibleView(withId(R.id.signin_settings_card));
        View view =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> {
                            return mSettingsActivityTestRule
                                    .getActivity()
                                    .findViewById(R.id.signin_settings_card);
                        });
        Assert.assertNotNull("No identity error card view found.", view);
        return view;
    }
}