chromium/chrome/browser/ui/android/webid/internal/java/src/org/chromium/chrome/browser/ui/android/webid/AccountSelectionJUnitTestBase.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.ui.android.webid;

import android.content.res.Resources;
import android.graphics.Color;
import android.view.View;

import androidx.annotation.Px;
import androidx.test.ext.junit.rules.ActivityScenarioRule;

import org.junit.Before;
import org.junit.Rule;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;

import org.chromium.base.Callback;
import org.chromium.blink.mojom.RpContext;
import org.chromium.blink.mojom.RpMode;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.android.webid.AccountSelectionProperties.AccountProperties;
import org.chromium.chrome.browser.ui.android.webid.AccountSelectionProperties.ItemProperties;
import org.chromium.chrome.browser.ui.android.webid.data.Account;
import org.chromium.chrome.browser.ui.android.webid.data.ClientIdMetadata;
import org.chromium.chrome.browser.ui.android.webid.data.IdentityCredentialTokenError;
import org.chromium.chrome.browser.ui.android.webid.data.IdentityProviderData;
import org.chromium.chrome.browser.ui.android.webid.data.IdentityProviderMetadata;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.image_fetcher.ImageFetcher;
import org.chromium.ui.base.TestActivity;
import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
import org.chromium.url.GURL;
import org.chromium.url.JUnitTestGURLs;

/** Common test fixtures for AccountSelection Robolectric JUnit tests. */
public class AccountSelectionJUnitTestBase {
    @Parameter(0)
    public @RpMode.EnumType int mRpMode;

    @Rule
    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
            new ActivityScenarioRule<>(TestActivity.class);

    class RpContextEntry {
        public int mValue;
        public int mTitleId;

        RpContextEntry(@RpContext.EnumType int value, int titleId) {
            mValue = value;
            mTitleId = titleId;
        }
    }

    protected static final String TEST_ERROR_CODE = "invalid_request";
    protected static final int[] RP_CONTEXTS =
            new int[] {RpContext.SIGN_IN, RpContext.SIGN_UP, RpContext.USE, RpContext.CONTINUE};
    protected static final @Px int DESIRED_AVATAR_SIZE = 100;

    @Mock Callback<Account> mAccountCallback;
    @Mock AccountSelectionComponent.Delegate mMockDelegate;
    @Mock ImageFetcher mMockImageFetcher;
    @Mock BottomSheetController mMockBottomSheetController;
    @Mock Tab mTab;

    // Constants but this test base is used by parameterized tests. These can only be initialized
    // after parameterized test runner setup.
    String mTestEtldPlusOne;
    String mTestEtldPlusOne1;
    String mTestEtldPlusOne2;
    GURL mTestUrlTermsOfService;
    GURL mTestUrlPrivacyPolicy;
    GURL mTestIdpBrandIconUrl;
    GURL mTestRpBrandIconUrl;
    GURL mTestProfilePicUrl;
    GURL mTestConfigUrl;
    GURL mTestLoginUrl;
    GURL mTestErrorUrl;
    GURL mTestEmptyErrorUrl;
    Account mAnaAccount;
    Account mBobAccount;
    Account mCarlAccount;
    Account mNewUserAccount;
    Account mNoOneAccount;

    ClientIdMetadata mClientIdMetadata;
    IdentityCredentialTokenError mTokenError;
    IdentityCredentialTokenError mTokenErrorEmptyUrl;

    Resources mResources;
    PropertyModel mModel;
    ModelList mSheetAccountItems;
    View mContentView;
    IdentityProviderMetadata mIdpMetadata;
    IdentityProviderData mNewAccountsIdpSingleReturningAccount;
    IdentityProviderData mNewAccountsIdpSingleNewAccount;
    IdentityProviderData mNewAccountsIdpMultipleAccounts;
    AccountSelectionBottomSheetContent mBottomSheetContent;
    AccountSelectionMediator mMediator;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        // Note that these are not actual ETLD+1 values, but this is irrelevant for the purposes of
        // this test.
        mTestEtldPlusOne = JUnitTestGURLs.EXAMPLE_URL.getSpec();
        mTestEtldPlusOne1 = JUnitTestGURLs.URL_1.getSpec();
        mTestEtldPlusOne2 = JUnitTestGURLs.URL_2.getSpec();
        mTestUrlTermsOfService = JUnitTestGURLs.RED_1;
        mTestUrlPrivacyPolicy = JUnitTestGURLs.RED_2;
        mTestIdpBrandIconUrl = JUnitTestGURLs.RED_3;
        mTestRpBrandIconUrl = JUnitTestGURLs.RED_3;
        mTestProfilePicUrl = new GURL("https://profile-picture.com");
        mTestConfigUrl = new GURL("https://idp.com/fedcm.json");
        mTestLoginUrl = new GURL("https://idp.com/login");
        mTestErrorUrl = new GURL("https://idp.com/error");
        mTestEmptyErrorUrl = new GURL("");

        mAnaAccount =
                new Account(
                        "Ana",
                        "[email protected]",
                        "Ana Doe",
                        "Ana",
                        mTestProfilePicUrl,
                        /* pictureBitmap= */ null,
                        /* isSignIn= */ true,
                        /* isBrowserTrustedSignIn= */ true);
        mBobAccount =
                new Account(
                        "Bob",
                        "",
                        "Bob",
                        "",
                        mTestProfilePicUrl,
                        /* pictureBitmap= */ null,
                        /* isSignIn= */ true,
                        /* isBrowserTrustedSignIn= */ true);
        mCarlAccount =
                new Account(
                        "Carl",
                        "[email protected]",
                        "Carl Test",
                        ":)",
                        mTestProfilePicUrl,
                        /* pictureBitmap= */ null,
                        /* isSignIn= */ true,
                        /* isBrowserTrustedSignIn= */ true);
        mNewUserAccount =
                new Account(
                        "602214076",
                        "[email protected]",
                        "Sam E. Goto",
                        "Sam",
                        mTestProfilePicUrl,
                        /* pictureBitmap= */ null,
                        /* isSignIn= */ false,
                        /* isBrowserTrustedSignIn= */ false);
        mNoOneAccount =
                new Account(
                        "",
                        "",
                        "No Subject",
                        "",
                        mTestProfilePicUrl,
                        /* pictureBitmap= */ null,
                        /* isSignIn= */ true,
                        /* isBrowserTrustedSignIn= */ true);

        mClientIdMetadata =
                new ClientIdMetadata(
                        mTestUrlTermsOfService,
                        mTestUrlPrivacyPolicy,
                        mTestRpBrandIconUrl.getSpec());
        mTokenError = new IdentityCredentialTokenError(TEST_ERROR_CODE, mTestErrorUrl);
        mTokenErrorEmptyUrl = new IdentityCredentialTokenError(TEST_ERROR_CODE, mTestEmptyErrorUrl);

        mIdpMetadata =
                new IdentityProviderMetadata(
                        Color.BLUE,
                        Color.GREEN,
                        "https://icon-url.example",
                        mTestConfigUrl,
                        mTestLoginUrl,
                        /* supportsAddAccount= */ false);

        mNewAccountsIdpSingleReturningAccount =
                new IdentityProviderData(
                        mTestEtldPlusOne,
                        new Account[] {mAnaAccount},
                        mIdpMetadata,
                        mClientIdMetadata,
                        RpContext.SIGN_IN,
                        /* requestPermission= */ true,
                        /* hasLoginStatusMismatch= */ false);
        mNewAccountsIdpSingleNewAccount =
                new IdentityProviderData(
                        mTestEtldPlusOne,
                        new Account[] {mNewUserAccount},
                        mIdpMetadata,
                        mClientIdMetadata,
                        RpContext.SIGN_IN,
                        /* requestPermission= */ true,
                        /* hasLoginStatusMismatch= */ false);
        mNewAccountsIdpMultipleAccounts =
                new IdentityProviderData(
                        mTestEtldPlusOne,
                        new Account[] {mAnaAccount, mBobAccount},
                        mIdpMetadata,
                        mClientIdMetadata,
                        RpContext.SIGN_IN,
                        /* requestPermission= */ true,
                        /* hasLoginStatusMismatch= */ false);

        mActivityScenarioRule
                .getScenario()
                .onActivity(
                        activity -> {
                            mModel =
                                    new PropertyModel.Builder(
                                                    AccountSelectionProperties.ItemProperties
                                                            .ALL_KEYS)
                                            .build();
                            mSheetAccountItems = new ModelList();
                            mContentView =
                                    AccountSelectionCoordinator.setupContentView(
                                            activity, mModel, mSheetAccountItems, mRpMode);
                            activity.setContentView(mContentView);
                            mResources = activity.getResources();
                        });

        mBottomSheetContent =
                new AccountSelectionBottomSheetContent(
                        /* contentView= */ null,
                        /* bottomSheetController= */ null,
                        /* scrollOffsetSupplier= */ null,
                        mRpMode);
        mMediator =
                new AccountSelectionMediator(
                        mTab,
                        mMockDelegate,
                        mModel,
                        mSheetAccountItems,
                        mMockBottomSheetController,
                        mBottomSheetContent,
                        mMockImageFetcher,
                        DESIRED_AVATAR_SIZE,
                        mRpMode);
    }

    MVCListAdapter.ListItem buildAccountItem(Account account) {
        return new MVCListAdapter.ListItem(
                AccountSelectionProperties.ITEM_TYPE_ACCOUNT,
                new PropertyModel.Builder(AccountProperties.ALL_KEYS)
                        .with(AccountProperties.ACCOUNT, account)
                        .with(AccountProperties.ON_CLICK_LISTENER, mAccountCallback)
                        .build());
    }

    int countAllItems() {
        int count = 0;
        for (PropertyKey key : mModel.getAllProperties()) {
            if (containsItemOfType(mModel, key)) {
                count += 1;
            }
        }
        return count + mSheetAccountItems.size();
    }

    static boolean containsItemOfType(PropertyModel model, PropertyKey key) {
        if (key == ItemProperties.SPINNER_ENABLED) {
            return model.get((WritableBooleanPropertyKey) key);
        }
        return model.get((WritableObjectPropertyKey<PropertyModel>) key) != null;
    }
}