blob: b9da3988aef3e5a8b523f7a3c4ba0534f14bce18 [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.signin;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.pressBack;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.AllOf.allOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import android.view.View;
import androidx.test.espresso.ViewInteraction;
import androidx.test.filters.MediumTest;
import org.hamcrest.Matcher;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
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.chromium.base.Callback;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.MetricsUtils;
import org.chromium.base.test.util.MetricsUtils.HistogramDelta;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.incognito.interstitial.IncognitoInterstitialDelegate;
import org.chromium.chrome.browser.signin.account_picker.AccountConsistencyPromoAction;
import org.chromium.chrome.browser.signin.account_picker.AccountPickerBottomSheetCoordinator;
import org.chromium.chrome.browser.signin.account_picker.AccountPickerDelegate;
import org.chromium.chrome.browser.tabmodel.TabCreator;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.signin.AccountManagerFacadeProvider;
import org.chromium.components.signin.ProfileDataSource;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.base.GoogleServiceAuthError;
import org.chromium.components.signin.base.GoogleServiceAuthError.State;
import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
import org.chromium.components.signin.test.util.FakeProfileDataSource;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.DisableAnimationsTestRule;
/**
* Tests account picker bottom sheet of the web signin flow.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@Features.EnableFeatures({ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY})
@Batch(Batch.PER_CLASS)
public class AccountPickerBottomSheetTest {
private static class CustomFakeProfileDataSource extends FakeProfileDataSource {
int getNumberOfObservers() {
return mObservers.size();
}
}
private static final ProfileDataSource.ProfileData PROFILE_DATA1 =
new ProfileDataSource.ProfileData(
/* accountName= */ "test.account1@gmail.com", /* avatar= */ null,
/* fullName= */ "Test Account1", /* givenName= */ "Account1");
private static final ProfileDataSource.ProfileData PROFILE_DATA2 =
new ProfileDataSource.ProfileData(
/* accountName= */ "test.account2@gmail.com", /* avatar= */ null,
/* fullName= */ null, /* givenName= */ null);
// Disable animations to reduce flakiness.
@ClassRule
public static final DisableAnimationsTestRule sNoAnimationsRule =
new DisableAnimationsTestRule();
@ClassRule
public static final ChromeTabbedActivityTestRule sActivityTestRule =
new ChromeTabbedActivityTestRule();
@Rule
public final BlankCTATabInitialStateRule mInitialStateRule =
new BlankCTATabInitialStateRule(sActivityTestRule, false);
private final CustomFakeProfileDataSource mFakeProfileDataSource =
new CustomFakeProfileDataSource();
@Rule
public final AccountManagerTestRule mAccountManagerTestRule =
new AccountManagerTestRule(mFakeProfileDataSource);
@Mock
private TabModel mTabModelMock;
@Mock
private TabCreator mTabCreatorMock;
@Mock
private HelpAndFeedbackLauncher mHelpAndFeedbackLauncherMock;
@Mock
private AccountPickerDelegate mAccountPickerDelegateMock;
@Mock
private IncognitoInterstitialDelegate mIncognitoInterstitialDelegateMock;
@Captor
public ArgumentCaptor<Callback<String>> callbackArgumentCaptor;
private AccountPickerBottomSheetCoordinator mCoordinator;
@Before
public void setUp() {
initMocks(this);
when(mAccountPickerDelegateMock.isIncognitoModeEnabled()).thenReturn(true);
mAccountManagerTestRule.addAccount(PROFILE_DATA1);
mAccountManagerTestRule.addAccount(PROFILE_DATA2);
}
@Test
@MediumTest
public void testCollapsedSheetWithAccount() {
MetricsUtils.HistogramDelta accountConsistencyHistogram = new HistogramDelta(
"Signin.AccountConsistencyPromoAction", AccountConsistencyPromoAction.SHOWN);
buildAndShowCollapsedBottomSheet();
checkCollapsedAccountList(PROFILE_DATA1);
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testExpandedSheet() {
buildAndShowExpandedBottomSheet();
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(PROFILE_DATA1.getFullName())).check(matches(isDisplayed()));
onView(withText(PROFILE_DATA2.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(R.string.signin_add_account_to_device))
.check(matches(isDisplayed()));
onView(withText(R.string.signin_incognito_button)).check(matches(isDisplayed()));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
public void testExpandedSheetWithIncognitoModeDisabled() {
when(mAccountPickerDelegateMock.isIncognitoModeEnabled()).thenReturn(false);
buildAndShowExpandedBottomSheet();
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(PROFILE_DATA1.getFullName())).check(matches(isDisplayed()));
onView(withText(PROFILE_DATA2.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(R.string.signin_add_account_to_device))
.check(matches(isDisplayed()));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withText(R.string.signin_incognito_button)).check(doesNotExist());
}
@Test
@MediumTest
public void testCollapsedSheetWithZeroAccount() {
// As we have already added accounts in our current AccountManagerFacade mock
// Here since we want to test a zero account case, we would like to set up
// a new AccountManagerFacade mock with no account in it. The mock will be
// torn down in the end of the test in AccountManagerTestRule.
AccountManagerFacadeProvider.setInstanceForTests(
new FakeAccountManagerFacade(mFakeProfileDataSource));
TestThreadUtils.runOnUiThreadBlocking(() -> {
mCoordinator = new AccountPickerBottomSheetCoordinator(sActivityTestRule.getActivity(),
getBottomSheetController(), mAccountPickerDelegateMock,
mIncognitoInterstitialDelegateMock);
});
checkZeroAccountBottomSheet();
}
@Test
@MediumTest
public void testDismissCollapsedSheet() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.DISMISSED_BACK);
buildAndShowCollapsedBottomSheet();
onView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
BottomSheetController controller = getBottomSheetController();
Assert.assertTrue(controller.isSheetOpen());
Assert.assertEquals(2, mFakeProfileDataSource.getNumberOfObservers());
onView(isRoot()).perform(pressBack());
Assert.assertFalse(controller.isSheetOpen());
verify(mAccountPickerDelegateMock).onDismiss();
Assert.assertEquals(0, mFakeProfileDataSource.getNumberOfObservers());
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testDismissCollapsedSheetWithDismissButton() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.DISMISSED_BUTTON);
buildAndShowCollapsedBottomSheet();
onView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
BottomSheetController controller = getBottomSheetController();
Assert.assertTrue(controller.isSheetOpen());
Assert.assertEquals(2, mFakeProfileDataSource.getNumberOfObservers());
onView(withId(R.id.account_picker_dismiss_button)).perform(click());
Assert.assertFalse(controller.isSheetOpen());
verify(mAccountPickerDelegateMock).onDismiss();
Assert.assertEquals(0, mFakeProfileDataSource.getNumberOfObservers());
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testCollapsedSheetShowsWhenBackpressingOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
onView(isRoot()).perform(pressBack());
checkCollapsedAccountList(PROFILE_DATA1);
}
@Test
@MediumTest
public void testExpandedSheetShowsWhenBackpressingOnIncognitoInterstitial() {
buildAndShowExpandedBottomSheet();
onView(withText(R.string.signin_incognito_button)).perform(click());
onView(isRoot()).perform(pressBack());
onVisibleView(withText(R.string.signin_account_picker_dialog_title))
.check(matches(isDisplayed()));
onVisibleView(withText(R.string.signin_account_picker_bottom_sheet_subtitle))
.check(matches(isDisplayed()));
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(PROFILE_DATA1.getFullName())).check(matches(isDisplayed()));
onView(withText(PROFILE_DATA2.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(R.string.signin_add_account_to_device))
.check(matches(isDisplayed()));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
public void testAccountDisappearedOnCollapsedSheet() {
buildAndShowCollapsedBottomSheet();
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
CriteriaHelper.pollUiThread(() -> {
return !mCoordinator.getBottomSheetViewForTesting()
.findViewById(R.id.account_picker_selected_account)
.isShown();
});
checkZeroAccountBottomSheet();
}
@Test
@MediumTest
public void testAccountDisappearedOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
CriteriaHelper.pollUiThread(() -> {
return !mCoordinator.getBottomSheetViewForTesting()
.findViewById(R.id.account_picker_account_list)
.isShown();
});
checkZeroAccountBottomSheet();
}
@Test
@MediumTest
public void testAccountReappearedOnCollapsedSheet() {
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
TestThreadUtils.runOnUiThreadBlocking(() -> {
mCoordinator = new AccountPickerBottomSheetCoordinator(sActivityTestRule.getActivity(),
getBottomSheetController(), mAccountPickerDelegateMock,
mIncognitoInterstitialDelegateMock);
});
checkZeroAccountBottomSheet();
mAccountManagerTestRule.addAccount(PROFILE_DATA1.getAccountEmail());
checkCollapsedAccountList(PROFILE_DATA1);
}
@Test
@MediumTest
public void testOtherAccountsChangeOnCollapsedSheet() {
buildAndShowCollapsedBottomSheet();
checkCollapsedAccountList(PROFILE_DATA1);
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA2.getAccountEmail());
checkCollapsedAccountList(PROFILE_DATA1);
}
@Test
@MediumTest
public void testSelectedAccountChangeOnCollapsedSheet() {
buildAndShowCollapsedBottomSheet();
mAccountManagerTestRule.removeAccountAndWaitForSeeding(PROFILE_DATA1.getAccountEmail());
checkCollapsedAccountList(PROFILE_DATA2);
}
@Test
@MediumTest
public void testProfileDataUpdateOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
String newFullName = "New Full Name1";
String newGivenName = "New Given Name1";
TestThreadUtils.runOnUiThreadBlocking(() -> {
mFakeProfileDataSource.setProfileData(PROFILE_DATA1.getAccountEmail(),
new ProfileDataSource.ProfileData(
PROFILE_DATA1.getAccountEmail(), null, newFullName, newGivenName));
});
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).check(matches(isDisplayed()));
onVisibleView(withText(newFullName)).check(matches(isDisplayed()));
// Check that profile data update when the bottom sheet is expanded won't
// toggle out any hidden part.
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
public void testSignInDefaultAccountOnCollapsedSheet() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.SIGNED_IN_WITH_DEFAULT_ACCOUNT);
buildAndShowCollapsedBottomSheet();
clickContinueButtonAndCheckSignInInProgressSheet();
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testSignInAnotherAccount() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
buildAndShowExpandedBottomSheet();
onView(withText(PROFILE_DATA2.getAccountEmail())).perform(click());
CriteriaHelper.pollUiThread(mCoordinator.getBottomSheetViewForTesting().findViewById(
R.id.account_picker_selected_account)::isShown);
clickContinueButtonAndCheckSignInInProgressSheet();
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testSigninWithAddedAccount() {
MetricsUtils.HistogramDelta addAccountHistogram = new HistogramDelta(
"Signin.AccountConsistencyPromoAction", AccountConsistencyPromoAction.ADD_ACCOUNT);
MetricsUtils.HistogramDelta signedInWithAddedAccountHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.SIGNED_IN_WITH_ADDED_ACCOUNT);
MetricsUtils.HistogramDelta signedInWithNonDefaultAccountHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.SIGNED_IN_WITH_NON_DEFAULT_ACCOUNT);
buildAndShowExpandedBottomSheet();
onVisibleView(withText(R.string.signin_add_account_to_device)).perform(click());
verify(mAccountPickerDelegateMock).addAccount(callbackArgumentCaptor.capture());
ProfileDataSource.ProfileData profileDataAdded = new ProfileDataSource.ProfileData(
/* accountName= */ "test.account3@gmail.com", /* avatar= */ null,
/* fullName= */ null, /* givenName= */ null);
Callback<String> callback = callbackArgumentCaptor.getValue();
TestThreadUtils.runOnUiThreadBlocking(
() -> callback.onResult(profileDataAdded.getAccountEmail()));
clickContinueButtonAndCheckSignInInProgressSheet();
Assert.assertEquals(1, addAccountHistogram.getDelta());
Assert.assertEquals(1, signedInWithAddedAccountHistogram.getDelta());
Assert.assertEquals(0, signedInWithNonDefaultAccountHistogram.getDelta());
}
@Test
@MediumTest
public void testSignInGeneralError() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.GENERIC_ERROR_SHOWN);
// Throws a connection error during the sign-in action
doAnswer(invocation -> {
Callback<GoogleServiceAuthError> onSignInErrorCallback = invocation.getArgument(1);
onSignInErrorCallback.onResult(new GoogleServiceAuthError(State.CONNECTION_FAILED));
return null;
})
.when(mAccountPickerDelegateMock)
.signIn(eq(mAccountManagerTestRule.toCoreAccountInfo(
PROFILE_DATA1.getAccountEmail())),
any());
buildAndShowCollapsedBottomSheet();
clickContinueButtonAndWaitForErrorSheet();
onVisibleView(withText(R.string.signin_account_picker_bottom_sheet_error_title))
.check(matches(isDisplayed()));
onView(withText(R.string.signin_account_picker_general_error_subtitle))
.check(matches(isDisplayed()));
onView(withText(R.string.signin_account_picker_general_error_button))
.check(matches(isDisplayed()));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_signin_spinner_view)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testSignInAuthError() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.AUTH_ERROR_SHOWN);
CoreAccountInfo coreAccountInfo =
mAccountManagerTestRule.toCoreAccountInfo(PROFILE_DATA1.getAccountEmail());
// Throws an auth error during the sign-in action
doAnswer(invocation -> {
Callback<GoogleServiceAuthError> onSignInErrorCallback = invocation.getArgument(1);
onSignInErrorCallback.onResult(
new GoogleServiceAuthError(State.INVALID_GAIA_CREDENTIALS));
return null;
})
.when(mAccountPickerDelegateMock)
.signIn(eq(coreAccountInfo), any());
buildAndShowCollapsedBottomSheet();
clickContinueButtonAndWaitForErrorSheet();
onVisibleView(withText(R.string.signin_account_picker_bottom_sheet_error_title))
.check(matches(isDisplayed()));
onView(withText(R.string.signin_account_picker_auth_error_subtitle))
.check(matches(isDisplayed()));
onView(withText(R.string.auth_error_card_button)).check(matches(isDisplayed()));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_signin_spinner_view)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testTryAgainButtonOnSignInGeneralErrorSheet() {
CoreAccountInfo coreAccountInfo =
mAccountManagerTestRule.toCoreAccountInfo(PROFILE_DATA1.getAccountEmail());
// Throws a connection error during the sign-in action
doAnswer(invocation -> {
Callback<GoogleServiceAuthError> onSignInErrorCallback = invocation.getArgument(1);
onSignInErrorCallback.onResult(new GoogleServiceAuthError(State.CONNECTION_FAILED));
return null;
})
.when(mAccountPickerDelegateMock)
.signIn(eq(coreAccountInfo), any());
buildAndShowCollapsedBottomSheet();
clickContinueButtonAndWaitForErrorSheet();
doNothing().when(mAccountPickerDelegateMock).signIn(eq(coreAccountInfo), any());
// Clicking on the |Try again| button should perform the sign-in again and opens the sign-in
// in progress page.
clickContinueButtonAndCheckSignInInProgressSheet();
}
@Test
@MediumTest
public void testSigninAgainButtonOnSigninAuthErrorSheet() {
CoreAccountInfo coreAccountInfo =
mAccountManagerTestRule.toCoreAccountInfo(PROFILE_DATA1.getAccountEmail());
// Throws an auth error during the sign-in action
doAnswer(invocation -> {
Callback<GoogleServiceAuthError> onSignInErrorCallback = invocation.getArgument(1);
onSignInErrorCallback.onResult(
new GoogleServiceAuthError(State.INVALID_GAIA_CREDENTIALS));
return null;
})
.when(mAccountPickerDelegateMock)
.signIn(eq(coreAccountInfo), any());
buildAndShowCollapsedBottomSheet();
clickContinueButtonAndWaitForErrorSheet();
doAnswer(invocation -> {
Callback<Boolean> callback = invocation.getArgument(1);
callback.onResult(true);
return null;
})
.when(mAccountPickerDelegateMock)
.updateCredentials(eq(PROFILE_DATA1.getAccountEmail()), any());
onView(withText(R.string.auth_error_card_button)).perform(click());
checkCollapsedAccountList(PROFILE_DATA1);
}
@Test
@MediumTest
public void testAddAccountOnExpandedSheet() {
MetricsUtils.HistogramDelta accountConsistencyHistogram = new HistogramDelta(
"Signin.AccountConsistencyPromoAction", AccountConsistencyPromoAction.ADD_ACCOUNT);
buildAndShowExpandedBottomSheet();
onVisibleView(withText(R.string.signin_add_account_to_device)).perform(click());
verify(mAccountPickerDelegateMock).addAccount(callbackArgumentCaptor.capture());
ProfileDataSource.ProfileData profileDataAdded = new ProfileDataSource.ProfileData(
/* accountName= */ "test.account3@gmail.com", /* avatar= */ null,
/* fullName= */ null, /* givenName= */ null);
Callback<String> callback = callbackArgumentCaptor.getValue();
TestThreadUtils.runOnUiThreadBlocking(
() -> callback.onResult(profileDataAdded.getAccountEmail()));
checkCollapsedAccountList(profileDataAdded);
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
@Test
@MediumTest
public void testSelectAnotherAccountOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
onView(withText(PROFILE_DATA2.getAccountEmail())).perform(click());
checkCollapsedAccountList(PROFILE_DATA2);
}
@Test
@MediumTest
public void testSelectTheSameAccountOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).perform(click());
checkCollapsedAccountList(PROFILE_DATA1);
}
@Test
@MediumTest
public void testIncognitoOptionShownOnExpandedSheet() {
buildAndShowExpandedBottomSheet();
onView(withText(R.string.signin_incognito_button)).perform(click());
checkIncognitoInterstitialSheet();
}
@Test
@MediumTest
public void testLearnMoreButtonOnIncognitoInterstitial() {
buildAndShowExpandedBottomSheet();
onView(withText(R.string.signin_incognito_button)).perform(click());
onView(withId(R.id.incognito_interstitial_learn_more)).perform(click());
verify(mIncognitoInterstitialDelegateMock).openLearnMorePage();
}
@Test
@MediumTest
public void testContinueButtonOnIncognitoInterstitial() {
MetricsUtils.HistogramDelta accountConsistencyHistogram =
new HistogramDelta("Signin.AccountConsistencyPromoAction",
AccountConsistencyPromoAction.STARTED_INCOGNITO_SESSION);
buildAndShowExpandedBottomSheet();
onView(withText(R.string.signin_incognito_button)).perform(click());
onView(withId(R.id.incognito_interstitial_continue_button)).perform(click());
verify(mIncognitoInterstitialDelegateMock).openCurrentUrlInIncognitoTab();
Assert.assertEquals(1, accountConsistencyHistogram.getDelta());
}
private void checkIncognitoInterstitialSheet() {
onView(withText(R.string.incognito_interstitial_title)).check(matches(isDisplayed()));
onView(withId(R.id.incognito_interstitial_message)).check(matches(isDisplayed()));
onView(withId(R.id.incognito_interstitial_learn_more)).check(matches(isDisplayed()));
onView(withId(R.id.incognito_interstitial_continue_button)).check(matches(isDisplayed()));
onView(withId(R.id.account_picker_account_list)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
}
private void clickContinueButtonAndWaitForErrorSheet() {
View bottomSheetView = mCoordinator.getBottomSheetViewForTesting();
TestThreadUtils.runOnUiThreadBlocking(() -> {
bottomSheetView.findViewById(R.id.account_picker_continue_as_button).performClick();
});
CriteriaHelper.pollUiThread(() -> {
return !bottomSheetView.findViewById(R.id.account_picker_selected_account).isShown();
});
}
private void clickContinueButtonAndCheckSignInInProgressSheet() {
View bottomSheetView = mCoordinator.getBottomSheetViewForTesting();
TestThreadUtils.runOnUiThreadBlocking(() -> {
bottomSheetView.findViewById(R.id.account_picker_continue_as_button).performClick();
});
CriteriaHelper.pollUiThread(() -> {
return bottomSheetView.findViewById(R.id.account_picker_signin_spinner_view).isShown();
});
// TODO(https://crbug.com/1116348): Check AccountPickerDelegate.signIn() is called
// after solving AsyncTask wait problem in espresso
// Currently the ProgressBar animation cannot be disabled on android-marshmallow-arm64-rel
// bot with DisableAnimationsTestRule, we hide the ProgressBar manually here to enable
// checks of other elements on the screen.
// TODO(https://crbug.com/1115067): Delete this line once DisableAnimationsTestRule is
// fixed.
TestThreadUtils.runOnUiThreadBlocking(() -> {
bottomSheetView.findViewById(R.id.account_picker_signin_spinner_view)
.setVisibility(View.GONE);
});
onView(withText(R.string.signin_account_picker_bottom_sheet_signin_title))
.check(matches(isDisplayed()));
onView(withId(R.id.account_picker_account_list)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(not(isDisplayed())));
}
private void checkZeroAccountBottomSheet() {
onVisibleView(withText(PROFILE_DATA1.getAccountEmail())).check(doesNotExist());
onVisibleView(withText(PROFILE_DATA2.getAccountEmail())).check(doesNotExist());
onView(withId(R.id.account_picker_account_list)).check(matches(not(isDisplayed())));
onView(withId(R.id.account_picker_selected_account)).check(matches(not(isDisplayed())));
onVisibleView(withText(R.string.signin_add_account_to_device)).perform(click());
verify(mAccountPickerDelegateMock).addAccount(notNull());
}
private void checkCollapsedAccountList(ProfileDataSource.ProfileData profileData) {
CriteriaHelper.pollUiThread(mCoordinator.getBottomSheetViewForTesting().findViewById(
R.id.account_picker_selected_account)::isShown);
onVisibleView(withText(R.string.signin_account_picker_dialog_title))
.check(matches(isDisplayed()));
onVisibleView(withText(R.string.signin_account_picker_bottom_sheet_subtitle))
.check(matches(isDisplayed()));
onVisibleView(withText(profileData.getAccountEmail())).check(matches(isDisplayed()));
if (profileData.getFullName() != null) {
onVisibleView(withText(profileData.getFullName())).check(matches(isDisplayed()));
}
onVisibleView(withId(R.id.account_selection_mark)).check(matches(isDisplayed()));
String continueAsText =
sActivityTestRule.getActivity().getString(R.string.signin_promo_continue_as,
profileData.getGivenName() != null ? profileData.getGivenName()
: profileData.getAccountEmail());
onView(withText(continueAsText)).check(matches(isDisplayed()));
onView(withId(R.id.account_picker_dismiss_button)).check(matches(isDisplayed()));
onView(withId(R.id.account_picker_account_list)).check(matches(not(isDisplayed())));
}
private void buildAndShowCollapsedBottomSheet() {
TestThreadUtils.runOnUiThreadBlocking(() -> {
mCoordinator = new AccountPickerBottomSheetCoordinator(sActivityTestRule.getActivity(),
getBottomSheetController(), mAccountPickerDelegateMock, mTabModelMock,
mTabCreatorMock, mHelpAndFeedbackLauncherMock);
});
CriteriaHelper.pollUiThread(mCoordinator.getBottomSheetViewForTesting().findViewById(
R.id.account_picker_selected_account)::isShown);
}
private void buildAndShowExpandedBottomSheet() {
TestThreadUtils.runOnUiThreadBlocking(() -> {
mCoordinator = new AccountPickerBottomSheetCoordinator(sActivityTestRule.getActivity(),
getBottomSheetController(), mAccountPickerDelegateMock,
mIncognitoInterstitialDelegateMock);
});
CriteriaHelper.pollUiThread(mCoordinator.getBottomSheetViewForTesting().findViewById(
R.id.account_picker_selected_account)::isShown);
onView(withText(PROFILE_DATA1.getFullName())).perform(click());
}
private BottomSheetController getBottomSheetController() {
return sActivityTestRule.getActivity()
.getRootUiCoordinatorForTesting()
.getBottomSheetController();
}
private static ViewInteraction onVisibleView(Matcher<View> matcher) {
// Some view elements like PROFILE_DATA1 exist in both visible view and hidden view,
// withEffectiveVisibility(VISIBLE) is needed here to get only the visible view of the
// matcher.
return onView(allOf(matcher, withEffectiveVisibility(VISIBLE)));
}
}