chromium/ios/chrome/browser/policy/model/user_policy_egtest.mm

// Copyright 2022 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/testing/earl_grey/earl_grey_test.h"

#import <memory>

#import "base/functional/bind.h"
#import "base/ios/ios_util.h"
#import "base/strings/strcat.h"
#import "base/strings/string_util.h"
#import "base/strings/stringprintf.h"
#import "base/strings/sys_string_conversions.h"
#import "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#import "base/time/time.h"
#import "components/policy/core/common/cloud/cloud_policy_constants.h"
#import "components/policy/core/common/policy_pref_names.h"
#import "components/policy/core/common/policy_switches.h"
#import "components/policy/policy_constants.h"
#import "components/policy/proto/cloud_policy.pb.h"
#import "components/policy/test_support/embedded_policy_test_server.h"
#import "components/policy/test_support/policy_storage.h"
#import "components/policy/test_support/signature_provider.h"
#import "components/strings/grit/components_strings.h"
#import "google_apis/gaia/gaia_switches.h"
#import "ios/chrome/browser/policy/model/cloud/user_policy_constants.h"
#import "ios/chrome/browser/policy/model/policy_app_interface.h"
#import "ios/chrome/browser/policy/model/policy_earl_grey_utils.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/signin/model/fake_system_identity.h"
#import "ios/chrome/browser/ui/authentication/signin/signin_constants.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/authentication/signin_matchers.h"
#import "ios/chrome/browser/ui/authentication/views/views_constants.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_app_interface.h"
#import "ios/chrome/test/earl_grey/chrome_test_case.h"
#import "ios/chrome/test/earl_grey/test_switches.h"
#import "ios/chrome/test/scoped_eg_synchronization_disabler.h"
#import "ios/testing/earl_grey/app_launch_configuration.h"
#import "ios/testing/earl_grey/app_launch_manager.h"
#import "net/test/embedded_test_server/embedded_test_server.h"
#import "net/test/embedded_test_server/http_request.h"
#import "net/test/embedded_test_server/http_response.h"
#import "net/test/embedded_test_server/request_handler_util.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"

using policy_test_utils::SetPolicy;

namespace {

constexpr base::TimeDelta kWaitOnScheduledUserPolicyFetchInterval =
    base::Seconds(20);

std::string GetTestEmail() {
  return base::StrCat({"enterprise@", policy::SignatureProvider::kTestDomain1});
}

std::string GetTestDomain() {
  return std::string(policy::SignatureProvider::kTestDomain1);
}

// Handles the UserInfo fetch requests done during user policy fetch.
std::unique_ptr<net::test_server::HttpResponse> HandleUserInfoRequest(
    const net::test_server::HttpRequest& r) {
  auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
  http_response->set_content(base::StringPrintf(R"(
        {
          "email": "%s",
          "verified_email": true,
          "hd": "%s"
        })",
                                                GetTestEmail().c_str(),
                                                GetTestDomain().c_str()));
  return http_response;
}

// Sets up and starts the test policy server that handles the user policy
// requests.
void SetUpPolicyServer(policy::EmbeddedPolicyTestServer* policy_server) {
  policy::PolicyStorage* policy_storage = policy_server->policy_storage();

  // Set a policy value for testing.
  enterprise_management::CloudPolicySettings settings;
  settings.mutable_incognitomodeavailability()
      ->mutable_policy_options()
      ->set_mode(enterprise_management::PolicyOptions::MANDATORY);
  settings.mutable_incognitomodeavailability()->set_value(1);
  policy_storage->SetPolicyPayload(policy::dm_protocol::kChromeUserPolicyType,
                                   settings.SerializeAsString());

  policy_storage->add_managed_user("*");
  policy_storage->set_policy_user(GetTestEmail());
  policy_storage->signature_provider()->set_current_key_version(1);
  policy_storage->set_policy_invalidation_topic("test_policy_topic");
}

// Waits on user policy data to be accessible from the browser state.
void WaitOnUserPolicy(base::TimeDelta timeout) {
  // Wait for user policy fetch.
  ConditionBlock condition = ^{
    return [PolicyAppInterface hasUserPolicyDataInCurrentBrowserState];
  };
  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(timeout, condition),
             @"No user policy data found");
}

// Verifies from the UI and the policy store that the user policies are set.
void VerifyThatPoliciesAreSet() {
  WaitOnUserPolicy(kWaitOnScheduledUserPolicyFetchInterval);

  // Verify that the policy is set.
  GREYAssertTrue(
      [PolicyAppInterface
          hasUserPolicyInCurrentBrowserState:@"IncognitoModeAvailability"
                            withIntegerValue:1],
      @"No policy data for IncognitoModeAvailability");
}

// Verifies from the UI and the policy store that the user policies are not set.
void VerifyThatPoliciesAreNotSet() {
  // Verify that there is no policy data in the store.
  GREYAssertFalse([PolicyAppInterface hasUserPolicyDataInCurrentBrowserState],
                  @"There should not be user policy data in the store");
}

void ClearUserPolicyPrefs() {
  // Clears the user policy notification pref.
  [ChromeEarlGrey clearUserPrefWithName:policy::policy_prefs::
                                            kUserPolicyNotificationWasShown];
  // Clears the pref used used to determine the fetch interval.
  [ChromeEarlGrey
      clearUserPrefWithName:policy::policy_prefs::kLastPolicyCheckTime];
  [ChromeEarlGrey commitPendingUserPrefsWrite];
}

void VerifyTheNotificationUI() {
  // Swipe up to make sure that all the text content in the prompt is visible.
  [[EarlGrey
      selectElementWithMatcher:
          grey_accessibilityID(kConfirmationAlertTitleAccessibilityIdentifier)]
      performAction:grey_swipeFastInDirection(kGREYDirectionUp)];

  NSString* title =
      l10n_util::GetNSString(IDS_IOS_USER_POLICY_NOTIFICATION_NO_SIGNOUT_TITLE);
  NSString* subtitle = l10n_util::GetNSStringF(
      IDS_IOS_USER_POLICY_NOTIFICATION_NO_SIGNOUT_SUBTITLE,
      base::UTF8ToUTF16(std::string(policy::SignatureProvider::kTestDomain1)));

  // Verify the notification UI.
  [[EarlGrey selectElementWithMatcher:grey_text(title)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:grey_text(subtitle)]
      assertWithMatcher:grey_sufficientlyVisible()];
}

// Wait for the chrome management url to become visible in the web state
// without validating the content. The goal being to verify that the page was
// opened.
void WaitForVisibleChromeManagementURL() {
  // const GURL expectedURL(base::StrCat({kChromeUIManagementURL, "/"}));

  NSString* errorString = [NSString
      stringWithFormat:@"Failed waiting for web state"
                       @" with visible url %@ ",
                       base::SysUTF8ToNSString(kChromeUIManagementURL)];

  GREYCondition* waitForUrl = [GREYCondition
      conditionWithName:errorString
                  block:^{
                    return base::StartsWith(
                        [ChromeEarlGrey webStateVisibleURL].spec(),
                        kChromeUIManagementURL);
                  }];
  base::TimeDelta timeout = base::Seconds(5);
  bool visibleUrl = [waitForUrl waitWithTimeout:timeout.InSecondsF()];
  GREYAssert(visibleUrl, errorString);
}

}  // namespace

// Test suite for User Policy.
@interface UserPolicyTestCase : ChromeTestCase
@end

@implementation UserPolicyTestCase {
  std::unique_ptr<policy::EmbeddedPolicyTestServer> policy_test_server_;
  std::unique_ptr<net::EmbeddedTestServer> embedded_test_server_;
}

- (void)setUp {
  // Set up and start the local policy test server.
  policy_test_server_ = std::make_unique<policy::EmbeddedPolicyTestServer>();
  SetUpPolicyServer(policy_test_server_.get());
  if (!policy_test_server_->Start()) {
    // Use NOTREACHED() instead of GREYAssertTrue because GREYAssertTrue can
    // only be used after calling the -setUp method of the super class.
    NOTREACHED_IN_MIGRATION();
  }

  // Set up and start the local test server for other services.
  embedded_test_server_ = std::make_unique<net::EmbeddedTestServer>(
      net::EmbeddedTestServer::TYPE_HTTP);
  embedded_test_server_->RegisterRequestHandler(base::BindRepeating(
      &net::test_server::HandlePrefixedRequest, "/oauth2/v1/userinfo",
      base::BindRepeating(&HandleUserInfoRequest)));
  if (!embedded_test_server_->Start()) {
    // Use NOTREACHED() instead of GREYAssertTrue because GREYAssertTrue can
    // only be used after calling the -setUp method of the super class.
    NOTREACHED_IN_MIGRATION();
  }

  [super setUp];
}

- (void)tearDown {
  ClearUserPolicyPrefs();
  [PolicyAppInterface clearPolicies];
  [super tearDown];
}

- (AppLaunchConfiguration)appConfigurationForTestCase {
  AppLaunchConfiguration config = [self minimalAppConfigurationForTestCase];
  // Enable User Policy for both consent levels.
  config.features_enabled.push_back(
      policy::kUserPolicyForSigninAndNoSyncConsentLevel);
  config.features_enabled.push_back(
      policy::kUserPolicyForSigninOrSyncConsentLevel);
  return config;
}

- (AppLaunchConfiguration)minimalAppConfigurationForTestCase {
  AppLaunchConfiguration config;
  // Set the url of the DMServer to reach the local test server.
  config.additional_args.push_back(
      base::StrCat({"--", policy::switches::kDeviceManagementUrl, "=",
                    policy_test_server_->GetServiceURL().spec()}));
  // Set the url of googleapis to reach the local test server.
  config.additional_args.push_back(
      base::StrCat({"--", switches::kGoogleApisUrl, "=",
                    embedded_test_server_->base_url().spec()}));
  return config;
}

#pragma mark - Tests

// Tests that the user policies are fetched and activated when signing in with
// a managed account.
// TODO(crbug.com/331794057): Test fails.
- (void)DISABLED_testThatPoliciesAreFetchedOnSignIn {
  // Sign in with managed account to fetch user policies.
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  VerifyThatPoliciesAreSet();
}

// Tests that the user policies are cleared after sign out.
// TODO(crbug.com/331794057): Test fails.
- (void)DISABLED_testThatPoliciesAreClearedOnSignOut {
  // Sign in with managed account to fetch user policies.
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];
  VerifyThatPoliciesAreSet();

  // Verify that the policies are cleared on sign out.
  [ChromeEarlGrey signOutAndClearIdentities];
  VerifyThatPoliciesAreNotSet();
}

// Tests that the user policies previously fetched are loaded from the store
// when signed in at startup.
// TODO(crbug.com/331794057): Test fails.
- (void)DISABLED_testThatPoliciesAreLoadedFromStoreWhenSignedInAtStartup {
  // Sign in with managed account to fetch user policies.
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  VerifyThatPoliciesAreSet();

  // Restart the browser while keeping sign-in by preserving the identity of the
  // managed account.
  AppLaunchConfiguration config = [self minimalAppConfigurationForTestCase];
  // Enable User Policy for sign-in consent level exclusively.
  config.features_enabled.push_back(
      policy::kUserPolicyForSigninAndNoSyncConsentLevel);
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  config.additional_args.push_back(
      base::StrCat({"--", test_switches::kSignInAtStartup}));
  config.additional_args.push_back(
      std::string("-") + test_switches::kAddFakeIdentitiesAtStartup + "=" +
      [FakeSystemIdentity encodeIdentitiesToBase64:@[ fakeManagedIdentity ]]);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Wait until the user policies are loaded from disk.
  WaitOnUserPolicy(kWaitOnScheduledUserPolicyFetchInterval);

  // Verifiy that the policies that were fetched in the previous session are
  // loaded from the cache at startup.
  VerifyThatPoliciesAreSet();
}

// TODO(crbug.com/40247145): Tests that the user policies are fetched when the
// user decides to "Continue" in the notification dialog.
- (void)DISABLED_testUserPolicyNotificationWithAcceptChoice {
  // Clear the prefs related to user policy to make sure that the notification
  // isn't skipped and that the fetch is started within the minimal schedule
  // interval.
  ClearUserPolicyPrefs();

  // Restart the app to disable user policy and allow signing in with the
  // managed account.
  AppLaunchConfiguration config;
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Sign in with the managed account. This won't trigger the user policy fetch.
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGreyUI signinWithFakeIdentity:fakeManagedIdentity];

  [ChromeEarlGrey commitPendingUserPrefsWrite];

  // Restart the browser while keeping signed-in by preserving the identity of
  // the managed account.
  config = [self appConfigurationForTestCase];
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  config.additional_args.push_back(
      base::StrCat({"--", test_switches::kSignInAtStartup}));
  config.additional_args.push_back(
      std::string("-") + test_switches::kAddFakeIdentitiesAtStartup + "=" +
      [FakeSystemIdentity encodeIdentitiesToBase64:@[ fakeManagedIdentity ]]);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Verify that the notification dialog is there.
  VerifyTheNotificationUI();

  // Tap on the "Continue" button to dismiss the alert dialog and start the user
  // policy fetch.
  NSString* continueLabel =
      l10n_util::GetNSString(IDS_IOS_ENTERPRISE_SIGNED_OUT_CONTINUE);
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(
                                   grey_accessibilityLabel(continueLabel),
                                   grey_accessibilityTrait(
                                       UIAccessibilityTraitButton),
                                   nil)] performAction:grey_tap()];

  // Wait for user policy fetch. This will take at least 5 seconds which
  // corresponds to the minimal user policy fetch delay when triggering the
  // fetch at startup.
  WaitOnUserPolicy(kWaitOnScheduledUserPolicyFetchInterval);

  // Verifiy that the policies were fetched and loaded.
  VerifyThatPoliciesAreSet();
}

// Tests that the learn more page is displayed when choosing that option in the
// notice dialog.
// TODO(crbug.com/40071362): reenable this test.
- (void)DISABLED_testUserPolicyNotificationWithLearnMoreChoice {
  // Clear the prefs related to user policy to make sure that the notification
  // isn't skipped and that the fetch is started within the minimal schedule
  // interval.
  ClearUserPolicyPrefs();

  // Restart the app to disable user policy and allow signing in with the
  // managed account.
  AppLaunchConfiguration config;
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Sign in with the managed account. This won't trigger the user policy fetch.
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGrey signinWithFakeIdentity:fakeManagedIdentity];

  // Restart the browser while keeping sign-in by preserving the identity of the
  // managed account.
  config = [self appConfigurationForTestCase];
  config.relaunch_policy = ForceRelaunchByCleanShutdown;
  config.additional_args.push_back(
      base::StrCat({"--", test_switches::kSignInAtStartup}));
  config.additional_args.push_back(
      std::string("-") + test_switches::kAddFakeIdentitiesAtStartup + "=" +
      [FakeSystemIdentity encodeIdentitiesToBase64:@[ fakeManagedIdentity ]]);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  // Verify that the notification dialog is there.
  VerifyTheNotificationUI();

  // Tap on the "Sign Out and Clear Data" button to dismiss the alert dialog
  // without triggering the user policy fetch.
  NSString* label =
      l10n_util::GetNSString(IDS_IOS_ENTERPRISE_SIGNED_OUT_LEARN_MORE);
  [[EarlGrey
      selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(label),
                                          grey_accessibilityTrait(
                                              UIAccessibilityTraitButton),
                                          nil)] performAction:grey_tap()];

  WaitForVisibleChromeManagementURL();
}

// Tests that the managed accout confirmation dialog is shown in the sign-in
// flow with its contextual and specific content when user policies are enabled.
// TODO(crbug.com/331794057): Test fails.
- (void)DISABLED_testSigninFlowConfirmationDialogWhenUserPolicyAndSignin {
  AppLaunchConfiguration config = [self minimalAppConfigurationForTestCase];
  // Enable User Policy for sign-in consent level exclusively.
  config.features_enabled.push_back(
      policy::kUserPolicyForSigninAndNoSyncConsentLevel);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];

  [SigninEarlGrey addFakeIdentity:fakeManagedIdentity];

  [self startSigninFlowUpToConfirmationDialogWithIdentity:fakeManagedIdentity];

  // Disable egtest synchronization to avoid infinite spinner loop.
  ScopedSynchronizationDisabler disabler;

  NSString* title = l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_TITLE);
  NSString* subtitle = l10n_util::GetNSStringF(
      IDS_IOS_MANAGED_SIGNIN_WITH_USER_POLICY_SUBTITLE,
      base::UTF8ToUTF16(std::string(policy::SignatureProvider::kTestDomain1)));

  // Verify the notification UI.
  [[EarlGrey selectElementWithMatcher:grey_text(title)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:grey_text(subtitle)]
      assertWithMatcher:grey_sufficientlyVisible()];
  [[EarlGrey selectElementWithMatcher:
                 grey_allOf(grey_accessibilityID(@"CancelAlertAction"),
                            [ChromeMatchersAppInterface
                                buttonWithAccessibilityLabelID:IDS_CANCEL],
                            nil)] assertWithMatcher:grey_sufficientlyVisible()];

  // Complete the sign-in flow by completing the confirmation dialog.
  id<GREYMatcher> acceptButton = [ChromeMatchersAppInterface
      buttonWithAccessibilityLabelID:
          IDS_IOS_MANAGED_SIGNIN_WITH_USER_POLICY_CONTINUE_BUTTON_LABEL];
  [ChromeEarlGrey waitForMatcher:acceptButton];
  [[EarlGrey selectElementWithMatcher:acceptButton] performAction:grey_tap()];

  // Verify that the confirmation dialog was dismissed.
  [[EarlGrey selectElementWithMatcher:grey_text(title)]
      assertWithMatcher:grey_notVisible()];

  // Verify that the flow was successful by validating that the account is
  // signed in after accepting from the confirmation dialog.
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeManagedIdentity];
}

// Tests that when user policies are enabled, in the sign-in flow, sign-in error
// popup isn't shown after cancelling the managed accout confirmation dialog.
- (void)testCancelSigninFlowConfirmationDialogWhenUserPolicyAndSignin {
  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];

  [SigninEarlGrey addFakeIdentity:fakeManagedIdentity];

  [self startSigninFlowUpToConfirmationDialogWithIdentity:fakeManagedIdentity];

  // Disable egtest synchronization to avoid infinite spinner loop.
  ScopedSynchronizationDisabler disabler;

  // Cancel the sign-in flow by tapping on the cancel button.
  id<GREYMatcher> cancelButton = grey_allOf(
      grey_accessibilityID(@"CancelAlertAction"),
      [ChromeMatchersAppInterface buttonWithAccessibilityLabelID:IDS_CANCEL],
      grey_sufficientlyVisible(), nil);
  [ChromeEarlGrey waitForMatcher:cancelButton];
  [[EarlGrey selectElementWithMatcher:cancelButton] performAction:grey_tap()];

  // Verify that the confirmation dialog was dismissed.
  NSString* title = l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_TITLE);
  [[EarlGrey selectElementWithMatcher:grey_text(title)]
      assertWithMatcher:grey_notVisible()];

  // Verify that no sign-in error alert action is shown.
  [ChromeEarlGrey
      waitForMatcher:chrome_test_util::WebSigninPrimaryButtonMatcher()];
  NSString* errorTitle = l10n_util::GetNSString(IDS_IOS_WEBSIGN_ERROR_TITLE);
  [[EarlGrey selectElementWithMatcher:grey_text(errorTitle)]
      assertWithMatcher:grey_notVisible()];

  // Verify that the flow was cancelled by validating that the account is
  // not signed in after accepting from the confirmation dialog.
  [SigninEarlGrey verifySignedOut];
}

// Tests that the managed account confirmation dialog isn't shown if the browser
// is already managed. Only applies for the sign-in consent level.
// TODO(crbug.com/331794057): Test fails on device.
#if !TARGET_IPHONE_SIMULATOR
#define MAYBE_testSigninFlowConfirmationDialogNotShownWhenAlreadyBrowserPolicies \
  DISABLED_testSigninFlowConfirmationDialogNotShownWhenAlreadyBrowserPolicies
#else
#define MAYBE_testSigninFlowConfirmationDialogNotShownWhenAlreadyBrowserPolicies \
  testSigninFlowConfirmationDialogNotShownWhenAlreadyBrowserPolicies
#endif
- (void)
    MAYBE_testSigninFlowConfirmationDialogNotShownWhenAlreadyBrowserPolicies {
  AppLaunchConfiguration config = [self minimalAppConfigurationForTestCase];
  // Enable User Policy for sign-in consent level exclusively.
  config.features_enabled.push_back(
      policy::kUserPolicyForSigninAndNoSyncConsentLevel);
  [[AppLaunchManager sharedManager] ensureAppLaunchedWithConfiguration:config];

  FakeSystemIdentity* fakeManagedIdentity = [FakeSystemIdentity
      identityWithEmail:base::SysUTF8ToNSString(GetTestEmail())];
  [SigninEarlGrey addFakeIdentity:fakeManagedIdentity];

  // Set a policy to put the browser under management before signing in with the
  // managed account.
  SetPolicy(false, policy::key::kAutofillAddressEnabled);

  // Sign-in with the UI flow.
  [self startSigninFlowUpToConfirmationDialogWithIdentity:fakeManagedIdentity];

  // Disable egtest synchronization to avoid infinite spinner loop.
  ScopedSynchronizationDisabler disabler;

  // Verify that there is no confirmation dialog.
  NSString* dialogTitle = l10n_util::GetNSString(IDS_IOS_MANAGED_SIGNIN_TITLE);
  [[EarlGrey selectElementWithMatcher:grey_text(dialogTitle)]
      assertWithMatcher:grey_notVisible()];

  // Verify that the flow was successful by validating that the account is
  // signed in.
  [SigninEarlGrey verifySignedInWithFakeIdentity:fakeManagedIdentity];
}

#pragma mark - Private

// Starts the sign-in flow up to the point where it may ask for the confirmation
// dialog.
- (void)startSigninFlowUpToConfirmationDialogWithIdentity:
    (FakeSystemIdentity*)identity {
  // Open sign-in flow.
  [ChromeEarlGreyUI openSettingsMenu];
  [ChromeEarlGreyUI
      tapSettingsMenuButton:chrome_test_util::SettingsSignInRowMatcher()];

  // Proceed with sign-in.
  [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                          WebSigninPrimaryButtonMatcher()]
      performAction:grey_tap()];
}

@end