chromium/ios/chrome/browser/ui/authentication/signout_action_sheet/signout_action_sheet_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/strings/strcat.h"
#import "base/strings/sys_string_conversions.h"
#import "components/policy/core/common/policy_loader_ios_constants.h"
#import "ios/chrome/browser/metrics/model/metrics_app_interface.h"
#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h"
#import "ios/chrome/browser/shared/public/features/features.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/google_services/google_services_settings_constants.h"
#import "ios/chrome/browser/ui/settings/google_services/manage_accounts/accounts_table_view_controller_constants.h"
#import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_constants.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/testing/earl_grey/app_launch_configuration.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_mac.h"

namespace {

void VerifySignoutDialogShownForManagedAccount() {
  NSString* title = l10n_util::GetNSString(
      IDS_IOS_SIGNOUT_CLEARS_DATA_DIALOG_TITLE_WITH_MANAGED_ACCOUNT);
  NSString* message = l10n_util::GetNSString(
      IDS_IOS_SIGNOUT_CLEARS_DATA_DIALOG_MESSAGE_WITH_MANAGED_ACCOUNT);

  [[EarlGrey selectElementWithMatcher:grey_text(title)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:grey_text(message)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

void ClickSignOutInAccountSettings() {
  [ChromeEarlGreyUI openSettingsMenu];

  // Open the "Account Settings" view.
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];

  // We're now in the "manage sync" view, and the signout button is at the very
  // bottom. Scroll there.
  id<GREYMatcher> scrollViewMatcher =
      grey_accessibilityID(kManageSyncTableViewAccessibilityIdentifier);
  [[EarlGrey selectElementWithMatcher:scrollViewMatcher]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];

  // Tap the "Sign out" button.
  [[EarlGrey selectElementWithMatcher:
                 grey_text(l10n_util::GetNSString(
                     IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_SIGN_OUT_ITEM))]
      performAction:grey_tap()];
}

}  // namespace

@interface SignoutActionSheetTestCase : ChromeTestCase

@end

@implementation SignoutActionSheetTestCase

#pragma mark - Tests

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config;
  // Enable the feature that shows the clear data on signout dialog for managed
  // accounts.
  config.features_enabled.push_back(kClearDeviceDataOnSignOutForManagedUsers);
  if ([self isRunningTest:@selector(testSignoutFromAccountsTableView)]) {
    // Once kIdentityDiscAccountMenu is launched, the sign out button in
    // AccountsTableView will be removed. It will be safe to remove this test at
    // that point. Also, testPopUpAccountsListViewOnSignOut covers the part of
    // correctly dismissing the view when the primary account is removed.
    config.features_disabled.push_back(kIdentityDiscAccountMenu);
  }
  return config;
}

- (AppLaunchConfiguration)managedAppConfigurationForTestCase {
  AppLaunchConfiguration config = self.appConfigurationForTestCase;
  config.additional_args.push_back(base::StrCat(
      {"-", base::SysNSStringToUTF8(kPolicyLoaderIOSConfigurationKey)}));
  config.additional_args.push_back(std::string("<dict></dict>"));
  return config;
}

// Tests the sign-out flow from the accounts table view. This test
// also makes sure the settings are not blocked after the sign-out.
// Related to crbug.com/1471942.
- (void)testSignoutFromAccountsTableView {
  FakeSystemIdentity* fakeIdentity = [FakeSystemIdentity fakeIdentity1];
  [SigninEarlGrey addFakeIdentity:fakeIdentity];

  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
  [ChromeEarlGreyUI openSettingsMenu];

  // Open the "manage sync" view.
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsAccountButton()];

  // Scroll to the bottom to view the "manage accounts" button.
  id<GREYMatcher> scrollViewMatcher =
      grey_accessibilityID(kManageSyncTableViewAccessibilityIdentifier);
  [[EarlGrey selectElementWithMatcher:scrollViewMatcher]
      performAction:grey_scrollToContentEdge(kGREYContentEdgeBottom)];

  // Tap the "manage accounts on This Device" button.
  [[EarlGrey selectElementWithMatcher:
                 grey_accessibilityLabel(l10n_util::GetNSString(
                     IDS_IOS_GOOGLE_ACCOUNT_SETTINGS_MANAGE_ACCOUNTS_ITEM))]
      performAction:grey_tap()];

  // The "manage accounts" view should be shown, tap on "Sign Out" button on
  // that page.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kSettingsAccountsTableViewSignoutCellId)]
      performAction:grey_tap()];

  [SigninEarlGrey verifySignedOut];

  // Verify the "manage accounts" view is popped.
  [[EarlGrey selectElementWithMatcher:grey_accessibilityID(
                                          kSettingsLegacyAccountsTableViewId)]
      assertWithMatcher:grey_notVisible()];

  // Verify the "manage sync" view is popped.
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kManageSyncTableViewAccessibilityIdentifier)]
      assertWithMatcher:grey_notVisible()];

  // Verify the account settings row is not showing in the settings menu.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::SettingsAccountButton()]
      assertWithMatcher:grey_notVisible()];
  // Open Google services settings to prove settings are not blocked.
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::GoogleServicesSettingsButton()];
  [ChromeEarlGreyUI waitForAppToIdle];
  [[EarlGrey
      selectElementWithMatcher:grey_accessibilityID(
                                   kGoogleServicesSettingsViewIdentifier)]
      assertWithMatcher:grey_notNil()];
}

// Tests the signout flow for managed users that require clearing data on
// signout. A dialog should be displayed, and clicking on the `Sign Out` button
// should sign the user out.
- (void)testSignoutConfirmationForManagedIdentity {
  GREYAssertNil([MetricsAppInterface setupHistogramTester],
                @"Cannot setup histogram tester.");
  [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];
  // Sign in with managed account.
  FakeSystemIdentity* fakeManagedIdentity =
      [FakeSystemIdentity fakeManagedIdentity];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  ClickSignOutInAccountSettings();
  VerifySignoutDialogShownForManagedAccount();

  // Click on signout and verify that data is cleared.
  [[EarlGrey
      selectElementWithMatcher:
          grey_allOf(chrome_test_util::AlertAction(l10n_util::GetNSString(
                         IDS_IOS_SIGNOUT_DIALOG_SIGN_OUT_BUTTON)),
                     grey_sufficientlyVisible(), nil)]
      performAction:grey_tap()];
  [SigninEarlGrey verifySignedOut];

  // Verify histogram metric for confirming signout is recorded.
  GREYAssertNil(
      [MetricsAppInterface
           expectCount:1
             forBucket:1
          forHistogram:@"Signin.SignoutAndClearDataFromManagedAccount"],
      @"Signin.SignoutAndClearDataFromManagedAccount metric was not recorded "
      @"when managed clicked cancel in the data clearing dialog shown on "
      @"signout.");

  [MetricsAppInterface stopOverridingMetricsAndCrashReportingForTesting];
  GREYAssertNil([MetricsAppInterface releaseHistogramTester],
                @"Cannot reset histogram tester.");
}

// Tests the signout flow for managed users that require clearing data on
// signout. A dialog should be displayed, and clicking on the `Cancel` button
// should keep the user signed in.
- (void)testCancelSignoutForManagedIdentity {
  GREYAssertNil([MetricsAppInterface setupHistogramTester],
                @"Cannot setup histogram tester.");
  [MetricsAppInterface overrideMetricsAndCrashReportingForTesting];

  // Sign in with managed account.
  FakeSystemIdentity* fakeManagedIdentity =
      [FakeSystemIdentity fakeManagedIdentity];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  ClickSignOutInAccountSettings();
  VerifySignoutDialogShownForManagedAccount();

  // Tap cancel and verify user is still signed in.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
      performAction:grey_tap()];
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeManagedIdentity];

  // Verify histogram metric for cancelling signout is recorded.
  GREYAssertNil(
      [MetricsAppInterface
           expectCount:1
             forBucket:0
          forHistogram:@"Signin.SignoutAndClearDataFromManagedAccount"],
      @"Signin.SignoutAndClearDataFromManagedAccount metric was not recorded "
      @"when managed clicked cancel in the data clearing dialog shown on "
      @"signout.");

  [MetricsAppInterface stopOverridingMetricsAndCrashReportingForTesting];
  GREYAssertNil([MetricsAppInterface releaseHistogramTester],
                @"Cannot reset histogram tester.");
}

// Tests the signout flow for managed users in a managed browser does not show
// the dialog for clearing data on sign-out.
- (void)testNoSignoutConfirmationForManagedIdentityInManagedBrowser {
  // Relaunch the app with managed config to take the configuration into
  // account.
  [[AppLaunchManager sharedManager]
      ensureAppLaunchedWithConfiguration:
          [self managedAppConfigurationForTestCase]];
  // Sign in with managed account.
  FakeSystemIdentity* fakeManagedIdentity =
      [FakeSystemIdentity fakeManagedIdentity];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  // The sign out button should directly sign out the user.
  ClickSignOutInAccountSettings();
  [SigninEarlGrey verifySignedOut];
}

@end