chromium/ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_egtest.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "base/test/ios/wait_util.h"
#import "build/branding_buildflags.h"
#import "components/password_manager/core/common/password_manager_pref_names.h"
#import "components/sync/base/features.h"
#import "ios/chrome/browser/shared/model/prefs/pref_names.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
#import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui_test_util.h"
#import "ios/chrome/browser/ui/settings/elements/elements_constants.h"
#import "ios/chrome/browser/ui/settings/password/password_details/password_details_table_view_constants.h"
#import "ios/chrome/browser/ui/settings/password/password_manager_egtest_utils.h"
#import "ios/chrome/browser/ui/settings/password/password_settings_app_interface.h"
#import "ios/chrome/browser/ui/settings/password/password_sharing/password_sharing_constants.h"
#import "ios/chrome/common/string_util.h"
#import "ios/chrome/common/ui/confirmation_alert/constants.h"
#import "ios/chrome/grit/ios_branded_strings.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
#import "ios/chrome/test/earl_grey/chrome_matchers.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/chrome/test/earl_grey/test_switches.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "ios/testing/earl_grey/earl_grey_test.h"
#import "ui/base/l10n/l10n_util.h"

#if !BUILDFLAG(GOOGLE_CHROME_BRANDING)
#import "components/password_manager/core/browser/password_manager_switches.h"
#endif  // !BUILDFLAG(GOOGLE_CHROME_BRANDING)

namespace {

using base::test::ios::kWaitForActionTimeout;
using password_manager_test_utils::EditDoneButton;
using password_manager_test_utils::kScrollAmount;
using password_manager_test_utils::NavigationBarEditButton;
using password_manager_test_utils::OpenPasswordManager;
using password_manager_test_utils::PasswordDetailsShareButtonMatcher;
using password_manager_test_utils::PasswordDetailsTableViewMatcher;
using password_manager_test_utils::SaveExamplePasskeyToStore;
using password_manager_test_utils::SavePasswordFormToProfileStore;

// Matcher for Password Sharing First Run.
id<GREYMatcher> PasswordSharingFirstRunMatcher() {
  return grey_accessibilityLabel(
      l10n_util::GetNSString(IDS_IOS_PASSWORD_SHARING_FIRST_RUN_TITLE));
}

// Matcher for the UITableView inside the Family Picker View.
id<GREYMatcher> FamilyPickerTableViewMatcher() {
  return grey_accessibilityID(kFamilyPickerTableViewID);
}

// Matcher for the Password Picker View.
id<GREYMatcher> PasswordPickerViewMatcher() {
  return grey_accessibilityID(kPasswordPickerViewID);
}

GREYElementInteraction* TapCredentialEntryWithDomain(NSString* domain) {
  return [[[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityID(domain),
                                          grey_accessibilityTrait(
                                              UIAccessibilityTraitButton),
                                          grey_sufficientlyVisible(), nil)]
         usingSearchAction:grey_scrollInDirection(kGREYDirectionDown,
                                                  kScrollAmount)
      onElementWithMatcher:grey_accessibilityID(kPasswordsTableViewID)]
      performAction:grey_tap()];
}

// TODO(crbug.com/348484044): Re-enable the test suite on ipad on iOS 17.
#define DISABLE_ON_IPAD_WITH_IOS_17                            \
  if (@available(iOS 17.0, *)) {                               \
    if ([ChromeEarlGrey isIPadIdiom]) {                        \
      EARL_GREY_TEST_DISABLED(@"Disabled for iPad on iOS 17"); \
    }                                                          \
  }
}  // namespace

// Test case for the Password Sharing flow.
@interface PasswordSharingTestCase : ChromeTestCase

- (GREYElementInteraction*)saveExamplePasswordToProfileStoreAndOpenDetails;

- (GREYElementInteraction*)saveExamplePasswordsToProfileStoreAndOpenDetails;

- (GREYElementInteraction*)saveExamplePasskeyToStoreAndOpenDetails;

- (GREYElementInteraction*)saveExamplePasskeyAndPasswordToStoreAndOpenDetails;

@end

@implementation PasswordSharingTestCase

- (GREYElementInteraction*)saveExamplePasswordToProfileStoreAndOpenDetails {
  // Mock successful reauth for opening the Password Manager.
  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                    ReauthenticationResult::kSuccess];

  SavePasswordFormToProfileStore();
  OpenPasswordManager();
  return TapCredentialEntryWithDomain(@"example.com");
}

- (GREYElementInteraction*)saveExamplePasswordsToProfileStoreAndOpenDetails {
  // Mock successful reauth for opening the Password Manager.
  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                    ReauthenticationResult::kSuccess];

  SavePasswordFormToProfileStore(/*password=*/@"password1",
                                 /*username=*/@"username1");
  SavePasswordFormToProfileStore(/*password=*/@"password2",
                                 /*username=*/@"username2");
  OpenPasswordManager();
  return TapCredentialEntryWithDomain(@"example.com, 2 accounts");
}

- (GREYElementInteraction*)saveExamplePasskeyToStoreAndOpenDetails {
  // Mock successful reauth for opening the Password Manager.
  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                    ReauthenticationResult::kSuccess];

  SaveExamplePasskeyToStore();
  OpenPasswordManager();
  return TapCredentialEntryWithDomain(@"example.com");
}

- (GREYElementInteraction*)saveExamplePasskeyAndPasswordToStoreAndOpenDetails {
  // Mock successful reauth for opening the Password Manager.
  [PasswordSettingsAppInterface setUpMockReauthenticationModule];
  [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult:
                                    ReauthenticationResult::kSuccess];

  SaveExamplePasskeyToStore();
  SavePasswordFormToProfileStore();
  OpenPasswordManager();
  return TapCredentialEntryWithDomain(@"example.com, 2 accounts");
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config;
  config.relaunch_policy = NoForceRelaunchAndResetState;
  // Make recipients fetcher return `FetchFamilyMembersRequestStatus::kSuccess`
  // by default. Individual tests can override it.
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kFamilyStatus + "=1");
#if !BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // Make tests run on unbranded builds.
  config.additional_args.push_back(
      std::string("-") + password_manager::kEnableShareButtonUnbranded);
#endif  // !BUILDFLAG(GOOGLE_CHROME_BRANDING)

  if ([self isRunningTest:@selector(testShareButtonDisabledWithJustPasskeys)] ||
      [self isRunningTest:@selector
            (testShareButtonEnabledWithMixOfPasswordsAndPasskeys)]) {
    config.features_enabled.push_back(syncer::kSyncWebauthnCredentials);
  }

  return config;
}

- (void)setUp {
  [super setUp];

  // Make sure the following pref is in its non-default state (which should be
  // the case for all tests that do not test the first run experience flow).
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];
  // Make sure the password sharing pref is in its default state.
  [ChromeEarlGrey
      setBoolValue:YES
       forUserPref:password_manager::prefs::kPasswordSharingEnabled];
}

- (void)tearDown {
  [PasswordSettingsAppInterface removeMockReauthenticationModule];

  // Reset preference to its non-default state (which should be the case
  // for all tests that do not test the first run experience flow).
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];
  // Reset the password sharing pref to its default state.
  [ChromeEarlGrey
      setBoolValue:YES
       forUserPref:password_manager::prefs::kPasswordSharingEnabled];

  [super tearDown];
}

- (void)testShareButtonVisibility {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testShareButtonVisibilityForSignedOutUser {
  DISABLE_ON_IPAD_WITH_IOS_17
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_not(grey_sufficientlyVisible())];
}

- (void)testShareButtonVisibilityForUserOptedInToAccountStorage {
  DISABLE_ON_IPAD_WITH_IOS_17
  FakeSystemIdentity* fake_identity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey signinWithFakeIdentity:fake_identity];

  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testShareButtonVisibilityWithSharingPolicyDisabled {
  DISABLE_ON_IPAD_WITH_IOS_17
  [ChromeEarlGrey
      setBoolValue:NO
       forUserPref:password_manager::prefs::kPasswordSharingEnabled];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  // Share button should be visible and display the policy info popup upon tap.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kEnterpriseInfoBubbleViewId)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testShareButtonVisibilityDuringPasswordEditing {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Share button should not be visible during password details editing.
  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_not(grey_sufficientlyVisible())];

  // Share button should be visible again after editing is finished.
  [[EarlGrey selectElementWithMatcher:EditDoneButton()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testShareButtonDisabledWithJustPasskeys {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasskeyToStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_not(grey_enabled())];
}

- (void)testShareButtonEnabledWithMixOfPasswordsAndPasskeys {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasskeyAndPasswordToStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_enabled()];
}

- (void)testFamilyPickerCancelFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kFamilyPickerCancelButtonID)]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testPasswordPickerCancelFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordsToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordPickerCancelButtonID)]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testFamilyPickerSwipeToDismissFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerTableViewID)]
      performAction:grey_swipeFastInDirection(kGREYDirectionDown)];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testFamilyPromoSwipeToDismissFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  // Override family status with `FetchFamilyMembersRequestStatus::kNoFamily`.
  AppLaunchConfiguration config = [self appConfigurationForTestCase];
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kFamilyStatus + "=3");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(kFamilyPromoViewID)]
      performAction:grey_swipeFastInDirection(kGREYDirectionDown)];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testPasswordPickerSwipeToDismissFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordsToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:PasswordPickerViewMatcher()]
      performAction:grey_swipeFastInDirection(kGREYDirectionDown)];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testSharingStatusSwipeToDismissFlow {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"[email protected]")]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerShareButtonID)]
      performAction:grey_tap()];

  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kSharingStatusViewID)]
      performAction:grey_swipeFastInDirection(kGREYDirectionDown)];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testFetchingRecipientsNoFamilyStatus {
  DISABLE_ON_IPAD_WITH_IOS_17
  // Override family status with `FetchFamilyMembersRequestStatus::kNoFamily`.
  AppLaunchConfiguration config = [self appConfigurationForTestCase];
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kFamilyStatus + "=3");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Check that the family promo view was displayed.
  [[EarlGrey selectElementWithMatcher:
                 grey_allOf(grey_accessibilityID(
                                kConfirmationAlertTitleAccessibilityIdentifier),
                            grey_accessibilityLabel(l10n_util::GetNSString(
                                IDS_IOS_PASSWORD_SHARING_FAMILY_PROMO_TITLE)),
                            nil)] assertWithMatcher:grey_sufficientlyVisible()];

  // Click the "Got It" button.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kConfirmationAlertPrimaryActionAccessibilityIdentifier)]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testTappingGotItInFamilyPromoInviteMembersView {
  DISABLE_ON_IPAD_WITH_IOS_17
  // Override family status with
  // `FetchFamilyMembersRequestStatus::kNoOtherFamilyMembers`.
  AppLaunchConfiguration config = [self appConfigurationForTestCase];
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kFamilyStatus + "=5");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Check that the family promo view was displayed.
  [[EarlGrey
      selectElementWithMatcher:
          grey_allOf(
              grey_accessibilityID(
                  kConfirmationAlertTitleAccessibilityIdentifier),
              grey_accessibilityLabel(l10n_util::GetNSString(
                  IDS_IOS_PASSWORD_SHARING_FAMILY_PROMO_INVITE_MEMBERS_TITLE)),
              nil)] assertWithMatcher:grey_sufficientlyVisible()];

  // Click the "Got It" button.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kConfirmationAlertPrimaryActionAccessibilityIdentifier)]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testFetchingRecipientsError {
  DISABLE_ON_IPAD_WITH_IOS_17
  // Override family status with `FetchFamilyMembersRequestStatus::kUnknown`.
  AppLaunchConfiguration config = [self appConfigurationForTestCase];
  config.additional_args.push_back(std::string("-") +
                                   test_switches::kFamilyStatus + "=0");
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  [ChromeEarlGrey setBoolValue:YES
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Check that the error view was displayed and close it.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityLabel(l10n_util::GetNSString(
                     IDS_IOS_PASSWORD_SHARING_FETCHING_RECIPIENTS_ERROR_TITLE))]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testPasswordSharingSuccess {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Make sure that the share button is disabled before the recipient selection
  // and enabled after.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerShareButtonID)]
      assertWithMatcher:grey_not(grey_enabled())];
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(@"[email protected]")]
      performAction:grey_tap()];
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerShareButtonID)]
      assertWithMatcher:grey_enabled()];

  // Initiate sharing and wait for the animation to finish.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerShareButtonID)]
      performAction:grey_tap()];
  // TODO(crbug.com/40275395): Override animation time for tests.
  GREYCondition* waitForAnimationEnding = [GREYCondition
      conditionWithName:@"Wait for sharing animation to end"
                  block:^{
                    NSError* error = nil;
                    [[EarlGrey
                        selectElementWithMatcher:
                            grey_allOf(
                                grey_accessibilityLabel(l10n_util::GetNSString(
                                    IDS_IOS_PASSWORD_SHARING_SUCCESS_TITLE)),
                                grey_kindOfClassName(@"UILabel"), nil)]
                        assertWithMatcher:grey_sufficientlyVisible()
                                    error:&error];
                    return error == nil;
                  }];
  GREYAssertTrue([waitForAnimationEnding
                     waitWithTimeout:kWaitForActionTimeout.InSecondsF()],
                 @"Animation did not finish.");

  // Dismiss the success status view and check that the password details view is
  // currently displayed.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kSharingStatusDoneButtonID)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testNavigationBetweenPasswordAndFamilyPicker {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordsToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Check that the next button is enabled by default.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordPickerNextButtonID)]
      assertWithMatcher:grey_enabled()];

  // Select second row and click "Next".
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityID(@"username2"),
                                          grey_sufficientlyVisible(), nil)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordPickerNextButtonID)]
      performAction:grey_tap()];

  // Tap "Back" in family picker view and confirm it opens password picker.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerBackButtonID)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(kFamilyPickerBackButtonID)]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordPickerNextButtonID)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testTappingFamilyPickerIneligibleRecipientInfoPopup {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Scroll down to the last recipient (the ineligible ones are on the bottom).
  [[EarlGrey selectElementWithMatcher:FamilyPickerTableViewMatcher()]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];

  // Tap on the info button next to the ineligible recipient row.
  [[EarlGrey
      selectElementWithMatcher:
          grey_allOf(grey_accessibilityID([NSString
                         stringWithFormat:@"%@ %@", kFamilyPickerInfoButtonID,
                                          @"[email protected]"]),
                     grey_kindOfClass([UIButton class]), nil)]
      performAction:grey_tap()];

  // Check that the info popup about ineligibility is visible.
  [[EarlGrey
      selectElementWithMatcher:
          grey_text(ParseStringWithLinks(
                        l10n_util::GetNSString(
                            IDS_IOS_PASSWORD_SHARING_FAMILY_MEMBER_INELIGIBLE))
                        .string)] assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testTappingCancelInFirstRunExperienceView {
  DISABLE_ON_IPAD_WITH_IOS_17
  [ChromeEarlGrey setBoolValue:NO
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Tap the cancel button.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kConfirmationAlertSecondaryActionAccessibilityIdentifier)]
      performAction:grey_tap()];

  // Check that the current view is the password details view.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsTableViewMatcher()]
      assertWithMatcher:grey_notNil()];

  // Tap the share button again and verify that the first run view is still
  // displayed since it was not acknowledged.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:PasswordSharingFirstRunMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testTappingShareInFirstRunExperienceView {
  DISABLE_ON_IPAD_WITH_IOS_17
  [ChromeEarlGrey setBoolValue:NO
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  // Tap the share button in the first run experience view.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityID(
                     kConfirmationAlertPrimaryActionAccessibilityIdentifier)]
      performAction:grey_tap()];

  // Check that the current view is the family picker view.
  [[EarlGrey selectElementWithMatcher:FamilyPickerTableViewMatcher()]
      assertWithMatcher:grey_notNil()];

  // Tap the cancel button.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kFamilyPickerCancelButtonID)]
      performAction:grey_tap()];

  // Tap the share button in password details view and verify that the first run
  // view will not be displayed anymore since it was acknowledged.
  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];
  [[EarlGrey selectElementWithMatcher:FamilyPickerTableViewMatcher()]
      assertWithMatcher:grey_notNil()];
}

- (void)testFirstRunExperienceViewDismissedForAuthentication {
  DISABLE_ON_IPAD_WITH_IOS_17
  [ChromeEarlGrey setBoolValue:NO
                   forUserPref:prefs::kPasswordSharingFlowHasBeenEntered];

  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:PasswordSharingFirstRunMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Background then foreground app so reauthentication UI is displayed.
  [[AppLaunchManager sharedManager] backgroundAndForegroundApp];

  // Check that first run experience is gone and password details is visible.
  [[EarlGrey selectElementWithMatcher:PasswordSharingFirstRunMatcher()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordDetailsViewControllerID)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testFamilyPickerViewDismissedForAuthentication {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:FamilyPickerTableViewMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Background then foreground app so reauthentication UI is displayed.
  [[AppLaunchManager sharedManager] backgroundAndForegroundApp];

  // Check that the family picker is gone and password details is visible.
  [[EarlGrey selectElementWithMatcher:FamilyPickerTableViewMatcher()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordDetailsViewControllerID)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

- (void)testPasswordPickerViewDismissedForAuthentication {
  DISABLE_ON_IPAD_WITH_IOS_17
  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
  [self saveExamplePasswordsToProfileStoreAndOpenDetails];

  [[EarlGrey selectElementWithMatcher:PasswordDetailsShareButtonMatcher()]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:PasswordPickerViewMatcher()]
      assertWithMatcher:grey_sufficientlyVisible()];

  // Background then foreground app so reauthentication UI is displayed.
  [[AppLaunchManager sharedManager] backgroundAndForegroundApp];

  // Check that the password picker is gone and password details is visible.
  [[EarlGrey selectElementWithMatcher:PasswordPickerViewMatcher()]
      assertWithMatcher:grey_nil()];

  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kPasswordDetailsViewControllerID)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

@end