chromium/ios/chrome/browser/ui/settings/password/password_manager_egtest_utils.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 "ios/chrome/browser/ui/settings/password/password_manager_egtest_utils.h"

#import "base/strings/string_number_conversions.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/ui/settings/password/password_checkup/password_checkup_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_settings/password_settings_constants.h"
#import "ios/chrome/browser/ui/settings/password/password_settings_app_interface.h"
#import "ios/chrome/browser/ui/settings/password/passwords_table_view_constants.h"
#import "ios/chrome/browser/ui/settings/password/reauthentication/reauthentication_constants.h"
#import "ios/chrome/browser/ui/settings/settings_root_table_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/testing/earl_grey/earl_grey_test.h"
#import "ui/base/l10n/l10n_util.h"

using chrome_test_util::ButtonWithAccessibilityLabel;

namespace {

// Returns the expected text of the Password Manager's Password Checkup cell for
// the given `state`.
NSString* GetTextForPasswordCheckUIState(PasswordCheckUIState state) {
  if (state == PasswordCheckStateRunning) {
    return l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP_ONGOING);
  } else {
    return l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP);
  }
}

// Returns the expected detail text of the Password Manager's Password Checkup
// cell for the given `state`. `number` is the number that is expected to be in
// the detail text. Depending on the `state`, it can be the number of sites and
// apps for which there are saved passwords or the number of insecure passwords.
NSString* GetDetailTextForPasswordCheckUIState(PasswordCheckUIState state,
                                               int number) {
  switch (state) {
    case PasswordCheckStateSafe:
      return [NSString
          stringWithFormat:
              @"%@. %@", @"Checked just now",
              l10n_util::GetNSString(
                  IDS_IOS_PASSWORD_CHECKUP_SAFE_STATE_ACCESSIBILITY_LABEL)];

    case PasswordCheckStateUnmutedCompromisedPasswords:
      return base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_PASSWORD_CHECKUP_COMPROMISED_COUNT, number));
    case PasswordCheckStateReusedPasswords:
      return l10n_util::GetNSStringF(IDS_IOS_PASSWORD_CHECKUP_REUSED_COUNT,
                                     base::NumberToString16(number));
    case PasswordCheckStateWeakPasswords:
      return base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_PASSWORD_CHECKUP_WEAK_COUNT, number));
    case PasswordCheckStateDismissedWarnings:
      return base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_PASSWORD_CHECKUP_DISMISSED_COUNT, number));
    case PasswordCheckStateDisabled:
    case PasswordCheckStateDefault:
      return l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP_DESCRIPTION);
    case PasswordCheckStateRunning:
      return base::SysUTF16ToNSString(l10n_util::GetPluralStringFUTF16(
          IDS_IOS_PASSWORD_CHECKUP_SITES_AND_APPS_COUNT, number));
    case PasswordCheckStateError:
    case PasswordCheckStateSignedOut:
      return l10n_util::GetNSString(IDS_IOS_PASSWORD_CHECKUP_ERROR);
  }
}

id<GREYMatcher> DeletePasswordConfirmationButton() {
  return grey_allOf(ButtonWithAccessibilityLabel(
                        l10n_util::GetNSString(IDS_IOS_DELETE_ACTION_TITLE)),
                    grey_interactable(), nullptr);
}

}  // anonymous namespace

namespace password_manager_test_utils {

#pragma mark - Matchers

id<GREYMatcher> PasswordManagerEmptyView() {
  return grey_text(
      l10n_util::GetNSString(IDS_IOS_SETTINGS_PASSWORD_EMPTY_TITLE));
}

id<GREYMatcher> PasswordCheckupCellForState(PasswordCheckUIState state,
                                            int number) {
  NSString* text = GetTextForPasswordCheckUIState(state);
  NSString* detail_text = GetDetailTextForPasswordCheckUIState(state, number);
  return grey_accessibilityLabel(
      [NSString stringWithFormat:@"%@, %@", text, detail_text]);
}

id<GREYMatcher> PasswordIssuesTableView() {
  return grey_accessibilityID(kPasswordIssuesTableViewID);
}

id<GREYMatcher> PasswordDetailPassword() {
  return chrome_test_util::TextFieldForCellWithLabelId(
      IDS_IOS_SHOW_PASSWORD_VIEW_PASSWORD);
}

id<GREYMatcher> NavigationBarEditButton() {
  return grey_allOf(chrome_test_util::ButtonWithAccessibilityLabelId(
                        IDS_IOS_NAVIGATION_BAR_EDIT_BUTTON),
                    grey_not(chrome_test_util::TabGridEditButton()),
                    grey_userInteractionEnabled(), nil);
}

id<GREYMatcher> EditDoneButton() {
  return grey_accessibilityID(kSettingsToolbarEditDoneButtonId);
}

id<GREYMatcher> EditPasswordConfirmationButton() {
  return grey_allOf(ButtonWithAccessibilityLabel(
                        l10n_util::GetNSString(IDS_IOS_CONFIRM_PASSWORD_EDIT)),
                    grey_interactable(), nullptr);
}

id<GREYMatcher> UsernameTextfieldForUsernameAndSites(NSString* username,
                                                     NSString* sites) {
  return grey_allOf(
      grey_accessibilityID([NSString
          stringWithFormat:@"%@%@%@", kUsernameTextfieldForPasswordDetailsID,
                           username, sites]),
      grey_interactable(), nullptr);
}

id<GREYMatcher> PasswordTextfieldForUsernameAndSites(NSString* username,
                                                     NSString* sites) {
  return grey_allOf(
      grey_accessibilityID([NSString
          stringWithFormat:@"%@%@%@", kPasswordTextfieldForPasswordDetailsID,
                           username, sites]),
      grey_interactable(), nullptr);
}

id<GREYMatcher> DeleteButtonForUsernameAndSites(NSString* username,
                                                NSString* sites) {
  return grey_allOf(
      grey_accessibilityID([NSString
          stringWithFormat:@"%@%@%@", kDeleteButtonForPasswordDetailsID,
                           username, sites]),
      grey_interactable(), nullptr);
}

id<GREYMatcher> ReauthenticationController() {
  return grey_accessibilityID(
      password_manager::kReauthenticationViewControllerAccessibilityIdentifier);
}

id<GREYMatcher> PasswordSettingsTableView() {
  return grey_accessibilityID(kPasswordsSettingsTableViewId);
}

id<GREYMatcher> PasswordDetailsTableViewMatcher() {
  return grey_accessibilityID(kPasswordDetailsTableViewID);
}

id<GREYMatcher> PasswordDetailsShareButtonMatcher() {
  return grey_accessibilityID(kPasswordShareButtonID);
}

GREYElementInteraction* GetInteractionForIssuesListItem(
    id<GREYMatcher> matcher,
    GREYDirection direction) {
  return [[EarlGrey
      selectElementWithMatcher:grey_allOf(matcher, grey_interactable(), nil)]
         usingSearchAction:grey_scrollInDirection(direction, kScrollAmount)
      onElementWithMatcher:PasswordIssuesTableView()];
}

GREYElementInteraction* GetInteractionForPasswordIssueEntry(
    NSString* domain,
    NSString* username,
    NSString* compromised_description) {
  NSString* accessibility_label;
  if (compromised_description) {
    accessibility_label =
        [NSString stringWithFormat:@"%@, %@, %@", domain, username,
                                   compromised_description];
  } else {
    accessibility_label =
        [NSString stringWithFormat:@"%@, %@", domain, username];
  }
  return GetInteractionForIssuesListItem(
      ButtonWithAccessibilityLabel(accessibility_label), kGREYDirectionDown);
}

#pragma mark - Saving passwords

void SavePasswordFormToProfileStore(NSString* password,
                                    NSString* username,
                                    NSString* origin) {
  GREYAssert(
      [PasswordSettingsAppInterface saveExamplePasswordToProfileStore:password
                                                             username:username
                                                               origin:origin],
      kPasswordStoreErrorMessage);
}

void SavePasswordFormToAccountStore(NSString* password,
                                    NSString* username,
                                    NSString* origin) {
  GREYAssert(
      [PasswordSettingsAppInterface saveExamplePasswordToAccountStore:password
                                                             username:username
                                                               origin:origin],
      kPasswordStoreErrorMessage);
}

void SaveCompromisedPasswordFormToProfileStore(NSString* password,
                                               NSString* username,
                                               NSString* origin) {
  GREYAssert([PasswordSettingsAppInterface
                 saveCompromisedPasswordToProfileStore:password
                                              username:username
                                                origin:origin],
             kPasswordStoreErrorMessage);
}

void SaveMutedCompromisedPasswordFormToProfileStore(NSString* origin,
                                                    NSString* username,
                                                    NSString* password) {
  GREYAssert([PasswordSettingsAppInterface
                 saveMutedCompromisedPasswordToProfileStore:password
                                                   username:username
                                                     origin:origin],
             kPasswordStoreErrorMessage);
}

#pragma mark - Saving passkeys

void SaveExamplePasskeyToStore(NSString* rpId,
                               NSString* userId,
                               NSString* username,
                               NSString* userDisplayName) {
  [PasswordSettingsAppInterface saveExamplePasskeyToStore:rpId
                                                   userId:userId
                                                 username:username
                                          userDisplayName:userDisplayName];
}

#pragma mark - Helpers

void OpenPasswordManager() {
  [ChromeEarlGreyUI openSettingsMenu];
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsMenuPasswordsButton()];
  // The settings page requested results from PasswordStore. Make sure they
  // have already been delivered by posting a task to PasswordStore's
  // background task runner and wait until it is finished. Because the
  // background task runner is sequenced, this means that previously posted
  // tasks are also finished when this function exits.
  [PasswordSettingsAppInterface passwordProfileStoreResultsCount];
}

void TapNavigationBarEditButton() {
  [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()]
      performAction:grey_tap()];
}

void DeleteCredential(NSString* username, NSString* sites) {
  [[EarlGrey
      selectElementWithMatcher:DeleteButtonForUsernameAndSites(username, sites)]
      performAction:grey_tap()];

  [[EarlGrey selectElementWithMatcher:DeletePasswordConfirmationButton()]
      performAction:grey_tap()];
}

}  // namespace password_manager_test_utils