chromium/chrome/browser/ui/views/profiles/profile_menu_view_browsertest.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/browser/ui/views/profiles/profile_menu_view.h"

#include <stddef.h>

#include "base/callback_list.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/with_feature_override.h"
#include "base/threading/thread_restrictions.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/profiles/profile_test_util.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_browser_test_base.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/signin/signin_ui_util.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/sync/test/integration/secondary_account_helper.h"
#include "chrome/browser/sync/test/integration/status_change_checker.h"
#include "chrome/browser/sync/test/integration/sync_service_impl_harness.h"
#include "chrome/browser/sync/test/integration/sync_test.h"
#include "chrome/browser/themes/test/theme_service_changed_waiter.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/profiles/profile_menu_coordinator.h"
#include "chrome/browser/ui/views/profiles/profile_menu_view_base.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
#include "chrome/browser/web_applications/test/os_integration_test_override_impl.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/test/web_app_test_utils.h"
#include "chrome/browser/web_applications/web_app_helpers.h"
#include "chrome/browser/web_applications/web_app_install_info.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/user_education/interactive_feature_promo_test.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/google/core/common/google_util.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
#include "components/supervised_user/core/browser/supervised_user_capabilities.h"
#include "components/supervised_user/core/common/features.h"
#include "components/supervised_user/test_support/supervised_user_signin_test_utils.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_user_settings.h"
#include "components/sync/test/fake_server_network_resources.h"
#include "components/user_education/common/feature_promo_controller.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/extension_registry.h"
#include "google_apis/gaia/gaia_switches.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/events/event_utils.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/webview/webview.h"
#include "ui/views/test/widget_activation_waiter.h"
#include "ui/views/test/widget_test.h"

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/lacros/account_manager/fake_account_manager_ui_dialog_waiter.h"
#include "chrome/browser/signin/signin_ui_delegate_impl_lacros.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/startup/browser_init_params.h"
#include "components/account_manager_core/chromeos/account_manager_facade_factory.h"
#include "components/account_manager_core/chromeos/account_manager_mojo_service.h"
#endif

namespace {

constexpr char kTestEmail[] =;

class UnconsentedPrimaryAccountChecker
    : public StatusChangeChecker,
      public signin::IdentityManager::Observer {};

Profile* CreateAdditionalProfile() {}

#if !BUILDFLAG(IS_CHROMEOS)

const char kPasswordManagerId[] =;
const char kPasswordManagerPWAUrl[] =;

std::unique_ptr<web_app::WebAppInstallInfo> CreatePasswordManagerWebAppInfo() {}

#endif

}  // namespace

class ProfileMenuViewTestBase {};

class ProfileMenuViewExtensionsTest
    : public ProfileMenuViewTestBase,
      public InteractiveFeaturePromoTestT<extensions::ExtensionBrowserTest> {};

// Make sure nothing bad happens when the browser theme changes while the
// ProfileMenuView is visible. Regression test for crbug.com/737470
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest, ThemeChanged) {}

// Profile chooser view should close when a tab is added.
// Regression test for http://crbug.com/792845
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest, CloseBubbleOnTadAdded) {}

// Profile chooser view should close when active tab is changed.
// Regression test for http://crbug.com/792845
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest,
                       CloseBubbleOnActiveTabChanged) {}

// Profile chooser view should close when active tab is closed.
// Regression test for http://crbug.com/792845
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest,
                       CloseBubbleOnActiveTabClosed) {}

// Used to test that the bubble widget is destroyed before the browser.
class BubbleWidgetDestroyedObserver : public views::WidgetObserver {};

// Profile chooser view should close when the last tab is closed.
// Regression test for http://crbug.com/792845
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest,
                       CloseBubbleOnLastTabClosed) {}

// Opening the profile menu dismisses any existing IPH.
// Regression test for https://crbug.com/1205901
IN_PROC_BROWSER_TEST_F(ProfileMenuViewExtensionsTest, CloseIPH) {}

// Test that sets up a primary account (without sync) and simulates a click on
// the signout button.
class ProfileMenuViewSignoutTest : public ProfileMenuViewTestBase,
                                   public SigninBrowserTestBase {};

#if BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(ProfileMenuViewSignoutTest, Signout) {
  ASSERT_TRUE(Signout());
  EXPECT_FALSE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync));
  EXPECT_FALSE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));
}
#endif

#if !BUILDFLAG(IS_CHROMEOS_LACROS)

// Wrapper class to add parametrized
// `switches::kExplicitBrowserSigninUIOnDesktop` feature tests.
class ProfileMenuViewSignoutTestWithExplicitBrowserSigninFeature
    : public ProfileMenuViewSignoutTest,
      public base::test::WithFeatureOverride {};

// Checks that signout opens a new logout tab.
IN_PROC_BROWSER_TEST_P(
    ProfileMenuViewSignoutTestWithExplicitBrowserSigninFeature,
    OpenLogoutTab) {}

// Checks that the NTP is navigated to the logout URL, instead of creating
// another tab.
IN_PROC_BROWSER_TEST_P(
    ProfileMenuViewSignoutTestWithExplicitBrowserSigninFeature,
    SignoutFromNTP) {}

INSTANTIATE_FEATURE_OVERRIDE_TEST_SUITE();

// Signout test that handles logout requests. The parameter indicates whether
// an error page is generated for the logout request.
// Params of the ProfileMenuViewSignoutTestWithNetwork:
// -- bool uno_enabled;
// -- bool has_network_error;
class ProfileMenuViewSignoutTestWithNetwork
    : public ProfileMenuViewSignoutTest,
      public testing::WithParamInterface<std::tuple<bool, bool>> {};

// Tests that the local signout is performed (tokens are deleted) only if the
// logout tab failed to load.
IN_PROC_BROWSER_TEST_P(ProfileMenuViewSignoutTestWithNetwork, Signout) {}

INSTANTIATE_TEST_SUITE_P();
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)

// Test suite that sets up a primary sync account in an error state and
// simulates a click on the sync error button.
class ProfileMenuViewSyncErrorButtonTest : public ProfileMenuViewTestBase,
                                           public InProcessBrowserTest {};

#if BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(ProfileMenuViewSyncErrorButtonTest, OpenReauthDialog) {
  FakeAccountManagerUI* account_manager_ui = GetFakeAccountManagerUI();
  ASSERT_TRUE(account_manager_ui);
  FakeAccountManagerUIDialogWaiter dialog_waiter(
      account_manager_ui, FakeAccountManagerUIDialogWaiter::Event::kReauth);

  ASSERT_TRUE(Reauth());

  dialog_waiter.Wait();
  EXPECT_TRUE(account_manager_ui->IsDialogShown());
  EXPECT_EQ(1,
            account_manager_ui->show_account_reauthentication_dialog_calls());
}
#else
IN_PROC_BROWSER_TEST_F(ProfileMenuViewSyncErrorButtonTest, OpenReauthTab) {}
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Test suite that sets up a non-sync account in an error state and simulates a
// click on the sync button.
// This is only relevant on Lacros, as this state does not exist on desktop.
class ProfileMenuViewSigninErrorButtonTest : public ProfileMenuViewTestBase,
                                             public InProcessBrowserTest {
 public:
  class MockSigninUiDelegate
      : public signin_ui_util::SigninUiDelegateImplLacros {
   public:
    MOCK_METHOD(void,
                ShowTurnSyncOnUI,
                (Profile * profile,
                 signin_metrics::AccessPoint access_point,
                 signin_metrics::PromoAction promo_action,
                 const CoreAccountId& account_id,
                 TurnSyncOnHelper::SigninAbortedMode signin_aborted_mode,
                 bool is_sync_promo),
                ());
  };

  ProfileMenuViewSigninErrorButtonTest()
      : delegate_auto_reset_(
            signin_ui_util::SetSigninUiDelegateForTesting(&mock_delegate_)) {}

  CoreAccountInfo account_info() const { return account_info_; }

  // InProcessBrowserTest:
  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    SetTargetBrowser(browser());

    // Add an account, non-syncing and in authentication error.
    signin::IdentityManager* identity_manager =
        IdentityManagerFactory::GetForProfile(browser()->profile());
    account_info_ = signin::MakePrimaryAccountAvailable(
        identity_manager, kTestEmail, signin::ConsentLevel::kSignin);
    signin::UpdatePersistentErrorOfRefreshTokenForAccount(
        identity_manager, account_info_.account_id,
        GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
            GoogleServiceAuthError::InvalidGaiaCredentialsReason::
                CREDENTIALS_REJECTED_BY_SERVER));
    ASSERT_TRUE(
        identity_manager->HasAccountWithRefreshTokenInPersistentErrorState(
            account_info_.account_id));
  }

  void ClickTurnOnSync() {
    OpenProfileMenu();
    // This test does not check that the sync button is displayed in the menu,
    // but this is tested in ProfileMenuClickTest.
    base::HistogramTester histogram_tester;
    static_cast<ProfileMenuView*>(profile_menu_view())
        ->OnSigninButtonClicked(
            account_info(),
            ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
            signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
    histogram_tester.ExpectUniqueSample(
        "Profile.Menu.ClickedActionableItem",
        ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
        /*expected_bucket_count=*/1);
  }

 protected:
  CoreAccountInfo account_info_;

  testing::StrictMock<MockSigninUiDelegate> mock_delegate_;
  base::AutoReset<signin_ui_util::SigninUiDelegate*> delegate_auto_reset_;
};

IN_PROC_BROWSER_TEST_F(ProfileMenuViewSigninErrorButtonTest, OpenReauthDialog) {
  FakeAccountManagerUI* account_manager_ui = GetFakeAccountManagerUI();
  ASSERT_TRUE(account_manager_ui);
  FakeAccountManagerUIDialogWaiter dialog_waiter(
      account_manager_ui, FakeAccountManagerUIDialogWaiter::Event::kReauth);

  ClickTurnOnSync();

  // Reauth is shown first.
  dialog_waiter.Wait();
  EXPECT_TRUE(account_manager_ui->IsDialogShown());
  EXPECT_EQ(1,
            account_manager_ui->show_account_reauthentication_dialog_calls());

  base::RunLoop loop;
  EXPECT_CALL(
      mock_delegate_,
      ShowTurnSyncOnUI(
          browser()->profile(),
          signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN,
          signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT,
          account_info().account_id,
          TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT,
          /*sync_promo=*/false))
      .WillOnce([&loop]() { loop.Quit(); });

  // Complete reauth.
  // Fake that this account was successfully reauthenticated via the UI.
  crosapi::AccountManagerMojoService* mojo_service =
      MaybeGetAshAccountManagerMojoServiceForTests();
  DCHECK(mojo_service);
  account_manager::AccountKey kAccountKey{account_info_.gaia,
                                          account_manager::AccountType::kGaia};
  mojo_service->OnAccountUpsertionFinishedForTesting(
      account_manager::AccountUpsertionResult::FromAccount(
          {kAccountKey, account_info_.email}));
  account_manager_ui->CloseDialog();

  // Wait until the Sync confirmation is shown.
  loop.Run();
}
#endif

#if !BUILDFLAG(IS_CHROMEOS)

class ProfileMenuViewSigninPendingTest : public ProfileMenuViewTestBase,
                                         public InProcessBrowserTest {};

IN_PROC_BROWSER_TEST_F(ProfileMenuViewSigninPendingTest, OpenReauthTab) {}

#endif  // !BUILDFLAG(IS_CHROMEOS)

// This class is used to test the existence, the correct order and the call to
// the correct action of the buttons in the profile menu. This is done by
// advancing the focus to each button and simulating a click. It is expected
// that each button records a histogram sample from
// |ProfileMenuViewBase::ActionableItem|.
//
// Subclasses have to implement |GetExpectedActionableItemAtIndex|. The test
// itself should contain the setup and a call to |RunTest|. Example test suite
// instantiation:
//
// class ProfileMenuClickTest_WithPrimaryAccount : public ProfileMenuClickTest {
//   ...
//   ProfileMenuViewBase::ActionableItem GetExpectedActionableItemAtIndex(
//      size_t index) override {
//     return ...;
//   }
// };
//
// IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_WithPrimaryAccount,
//  SetupAndRunTest) {
//   ... /* setup primary account */
//   RunTest();
// }
//
// INSTANTIATE_TEST_SUITE_P(
//   ,
//   ProfileMenuClickTest_WithPrimaryAccount,
//   ::testing::Range(0, num_of_actionable_items));
//

class ProfileMenuClickTest : public SyncTest,
                             public ProfileMenuViewTestBase,
                             public testing::WithParamInterface<size_t> {};

#define PROFILE_MENU_CLICK_TEST_WITH_FEATURE_STATES_F(                    \
    FixtureClass, actionable_item_list, test_case_name, enabled_features, \
    disabled_features)

// Specialized variant of `PROFILE_MENU_CLICK_TEST_WITH_FEATURE_STATES_F` with
// no features overrides.
#define PROFILE_MENU_CLICK_TEST_F(FixtureClass, actionable_item_list, \
                                  test_case_name)

// Specialized variant of `PROFILE_MENU_CLICK_TEST_WITH_FEATURE_STATES_F` using
// `ProfileMenuClickTest` as `FixtureClass`, and allowing to override features
// states.
#define PROFILE_MENU_CLICK_WITH_FEATURE_TEST(                                  \
    actionable_item_list, test_case_name, enabled_features, disabled_features)

// Specialized variant of `PROFILE_MENU_CLICK_WITH_FEATURE_TEST` with no
// features overrides, which uses `ProfileMenuClickTest` as fixture class.
#define PROFILE_MENU_CLICK_TEST(actionable_item_list, test_case_name)

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SingleProfileWithCustomName_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SingleProfileWithCustomName_UnoDisabled,
    ProfileMenuClickTest_SingleProfileWithCustomName_UnoDisabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SingleProfileWithCustomName_UnoEnabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SingleProfileWithCustomName_UnoEnabled,
    ProfileMenuClickTest_SingleProfileWithCustomName_UnoEnabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_MultipleProfiles_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_MultipleProfiles_UnoDisabled,
    ProfileMenuClickTest_MultipleProfiles_UnoDisabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_MultipleProfiles_UnoEnabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_MultipleProfiles_UnoEnabled,
    ProfileMenuClickTest_MultipleProfiles_UnoEnabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncEnabled_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncEnabled_UnoDisabled,
    ProfileMenuClickTest_SyncEnabled_UnoDisabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added to
// this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncEnabled_UnoEnabled[] =;

// TODO(crbug.com/341975308): re-enable test.
#if BUILDFLAG(IS_WIN)
#define MAYBE_ProfileMenuClickTest_SyncEnabled_UnoEnabled
#else
#define MAYBE_ProfileMenuClickTest_SyncEnabled_UnoEnabled
#endif
PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncEnabled_UnoEnabled,
    MAYBE_ProfileMenuClickTest_SyncEnabled_UnoEnabled,
    /*enabled_features=*/{} {

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncError_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncError_UnoDisabled,
    ProfileMenuClickTest_SyncError_UnoDisabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncError_UnoEnabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncError_UnoEnabled,
    ProfileMenuClickTest_SyncError_UnoEnabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncPaused_UnoDisabled[] =;

// TODO(crbug.com/40822972): flaky on Windows and Mac
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_ProfileMenuClickTest_SyncPaused_UnoDisabled
#else
#define MAYBE_ProfileMenuClickTest_SyncPaused_UnoDisabled
#endif
PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncPaused_UnoDisabled,
    MAYBE_ProfileMenuClickTest_SyncPaused_UnoDisabled,
    /*enabled_features=*/{} {

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SyncPaused_UnoEnabled[] =;

// TODO(crbug.com/40822972): flaky on Windows and Mac
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_ProfileMenuClickTest_SyncPaused_UnoEnabled
#else
#define MAYBE_ProfileMenuClickTest_SyncPaused_UnoEnabled
#endif
PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SyncPaused_UnoEnabled,
    MAYBE_ProfileMenuClickTest_SyncPaused_UnoEnabled,
    /*enabled_features=*/{} {

// Lacros doesn't allow to disable sign-in in regular profiles yet.
// TODO(crbug.com/40772644): re-enable this test once kSigninAllowed is
// no longer force set to true on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SigninDisallowed_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SigninDisallowed_UnoDisabled,
    ProfileMenuClickTest_SigninDisallowed_UnoDisabled,
    /*enabled_features=*/{}

IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_SigninDisallowed_UnoDisabled,
                       PRE_ProfileMenuClickTest_SigninDisallowed_UnoDisabled) {}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SigninDisallowed_UnoEnabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_SigninDisallowed_UnoEnabled,
    ProfileMenuClickTest_SigninDisallowed_UnoEnabled,
    /*enabled_features=*/{}

IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_SigninDisallowed_UnoEnabled,
                       PRE_ProfileMenuClickTest_SigninDisallowed_UnoEnabled) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)

// List of actionable items in the correct order as they appear in the menu with
// Uno disabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_WithUnconsentedPrimaryAccount_UnoDisabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_WithUnconsentedPrimaryAccount_UnoDisabled,
    ProfileMenuClickTest_WithUnconsentedPrimaryAccount_UnoDisabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled. If a new button is added to the menu, it should also be added
// to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_WithUnconsentedPrimaryAccount_UnoEnabled[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_WithUnconsentedPrimaryAccount_UnoEnabled,
    ProfileMenuClickTest_WithUnconsentedPrimaryAccount_UnoEnabled,
    /*enabled_features=*/{}

// List of actionable items in the correct order as they appear in the menu with
// Uno enabled in signin pending state. If a new button is added to the menu,
// it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_WithPendingAccount_UnoEnabled[] =;

// TODO(crbug.com/40822972): flaky on Windows and Mac
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
#define MAYBE_ProfileMenuClickTest_WithPendingAccount_UnoEnabled
#else
#define MAYBE_ProfileMenuClickTest_WithPendingAccount_UnoEnabled
#endif
PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_WithPendingAccount_UnoEnabled,
    MAYBE_ProfileMenuClickTest_WithPendingAccount_UnoEnabled,
    /*enabled_features=*/{}

// The kHideGuestModeForSupervisedUsers does not exist on Lacros, as Guest mode
// hiding for supervised users is inherited from OS-level signals.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_GuestProfileButtonAvailable_SignedInNotSupervised[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_GuestProfileButtonAvailable_SignedInNotSupervised,
    ProfileMenuClickTest_GuestProfileButtonAvailable_SignedInNotSupervised,
    /*enabled_features=*/{}

constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_GuestProfileButtonNotAvailable_SignedInSupervised[] =;

PROFILE_MENU_CLICK_WITH_FEATURE_TEST(
    kActionableItems_GuestProfileButtonNotAvailable_SignedInSupervised,
    ProfileMenuClickTest_GuestProfileButtonNotAvailable_SignedInSupervised,
    /*enabled_features=*/{}
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SecondaryProfileWithUnconsentedPrimaryAccount[] = {
        ProfileMenuViewBase::ActionableItem::kEditProfileButton,
        ProfileMenuViewBase::ActionableItem::kPasswordsButton,
        ProfileMenuViewBase::ActionableItem::kCreditCardsButton,
        ProfileMenuViewBase::ActionableItem::kAddressesButton,
        ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
        ProfileMenuViewBase::ActionableItem::kManageGoogleAccountButton,
        ProfileMenuViewBase::ActionableItem::kSignoutButton,
        ProfileMenuViewBase::ActionableItem::kManageProfilesButton,
        ProfileMenuViewBase::ActionableItem::kOtherProfileButton,
        ProfileMenuViewBase::ActionableItem::kGuestProfileButton,
        ProfileMenuViewBase::ActionableItem::kAddNewProfileButton,
        // The first button is added again to finish the cycle and test that
        // there are no other buttons at the end.
        ProfileMenuViewBase::ActionableItem::kEditProfileButton};

PROFILE_MENU_CLICK_TEST(
    kActionableItems_SecondaryProfileWithUnconsentedPrimaryAccount,
    ProfileMenuClickTest_SecondaryProfileWithUnconsentedPrimaryAccount) {
  UseSecondaryProfile();
  secondary_account_helper::SignInUnconsentedAccount(
      GetProfile(), &test_url_loader_factory_, "[email protected]");
  UnconsentedPrimaryAccountChecker(identity_manager()).Wait();
  // Check that the setup was successful.
  ASSERT_FALSE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync));
  ASSERT_TRUE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSignin));

  RunTest();
}

// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_UnconsentedPrimaryAccountError[] = {
        ProfileMenuViewBase::ActionableItem::kEditProfileButton,
        ProfileMenuViewBase::ActionableItem::kPasswordsButton,
        ProfileMenuViewBase::ActionableItem::kCreditCardsButton,
        ProfileMenuViewBase::ActionableItem::kAddressesButton,
        ProfileMenuViewBase::ActionableItem::kSigninAccountButton,
        ProfileMenuViewBase::ActionableItem::kManageGoogleAccountButton,
        ProfileMenuViewBase::ActionableItem::kManageProfilesButton,
        ProfileMenuViewBase::ActionableItem::kGuestProfileButton,
        ProfileMenuViewBase::ActionableItem::kAddNewProfileButton,
        // The first button is added again to finish the cycle and test that
        // there are no other buttons at the end.
        ProfileMenuViewBase::ActionableItem::kEditProfileButton};

PROFILE_MENU_CLICK_TEST(kActionableItems_UnconsentedPrimaryAccountError,
                        ProfileMenuClickTest_UnconsentedPrimaryAccountError) {
  AccountInfo account_info =
      signin::MakeAccountAvailable(identity_manager(), "[email protected]");
  signin::UpdatePersistentErrorOfRefreshTokenForAccount(
      identity_manager(), account_info.account_id,
      GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
          GoogleServiceAuthError::InvalidGaiaCredentialsReason::
              CREDENTIALS_REJECTED_BY_SERVER));
  identity_manager()->GetPrimaryAccountMutator()->SetPrimaryAccount(
      account_info.account_id, signin::ConsentLevel::kSignin);

  // Check that the setup was successful.
  ASSERT_FALSE(
      identity_manager()->HasPrimaryAccount(signin::ConsentLevel::kSync));
  ASSERT_EQ(account_info, identity_manager()->GetPrimaryAccountInfo(
                              signin::ConsentLevel::kSignin));
  ASSERT_TRUE(
      identity_manager()->HasAccountWithRefreshTokenInPersistentErrorState(
          account_info.account_id));

  RunTest();
}

// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
// The two additional profiles created in the PRE_ test should be disabled and
// thus, not appear in  this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_SecondaryProfilesDisabled[] = {
        ProfileMenuViewBase::ActionableItem::kEditProfileButton,
        ProfileMenuViewBase::ActionableItem::kPasswordsButton,
        ProfileMenuViewBase::ActionableItem::kCreditCardsButton,
        ProfileMenuViewBase::ActionableItem::kAddressesButton,
        ProfileMenuViewBase::ActionableItem::kSigninButton,
        // The first button is added again to finish the cycle and test that
        // there are no other buttons at the end.
        ProfileMenuViewBase::ActionableItem::kEditProfileButton};

PROFILE_MENU_CLICK_TEST(kActionableItems_SecondaryProfilesDisabled,
                        ProfileMenuClickTest_SecondaryProfilesDisabled) {
  // Check that the setup was successful.
  ASSERT_FALSE(g_browser_process->local_state()->GetBoolean(
      prefs::kLacrosSecondaryProfilesAllowed));

  RunTest();
}

IN_PROC_BROWSER_TEST_P(ProfileMenuClickTest_SecondaryProfilesDisabled,
                       PRE_ProfileMenuClickTest_SecondaryProfilesDisabled) {
  // Add two additional profiles, which later shouldn't be clickable.
  CreateAdditionalProfile();
  CreateAdditionalProfile();

  g_browser_process->local_state()->SetBoolean(
      prefs::kLacrosSecondaryProfilesAllowed, false);
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_IncognitoProfile[] =;

PROFILE_MENU_CLICK_TEST(kActionableItems_IncognitoProfile,
                        ProfileMenuClickTest_IncognitoProfile) {}

// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem kActionableItems_GuestProfile[] =;

PROFILE_MENU_CLICK_TEST(kActionableItems_GuestProfile,
                        ProfileMenuClickTest_GuestProfile) {}

#if BUILDFLAG(IS_CHROMEOS_LACROS)

class ProfileMenuClickTestGuestSession : public ProfileMenuClickTest {
 public:
  // Enable the guest session.
  void CreatedBrowserMainParts(
      content::BrowserMainParts* browser_main_parts) override {
    crosapi::mojom::BrowserInitParamsPtr init_params =
        chromeos::BrowserInitParams::GetForTests()->Clone();
    init_params->session_type = crosapi::mojom::SessionType::kGuestSession;
    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
    ProfileMenuClickTest::CreatedBrowserMainParts(browser_main_parts);
  }
};

// This tests the device guest session, which is not the same as the browser
// guest mode.
PROFILE_MENU_CLICK_TEST_F(ProfileMenuClickTestGuestSession,
                          kActionableItems_GuestProfile,
                          ProfileMenuClickTest_GuestSession) {
  SetTargetBrowser(browser());

  RunTest();
}

#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

#if !BUILDFLAG(IS_CHROMEOS)
class ProfileMenuClickTestWebApp : public ProfileMenuClickTest {};

// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
constexpr ProfileMenuViewBase::ActionableItem
    kActionableItems_PasswordManagerWebApp[] =;

PROFILE_MENU_CLICK_TEST_F(ProfileMenuClickTestWebApp,
                          kActionableItems_PasswordManagerWebApp,
                          ProfileMenuClickTest_PasswordManagerWebApp) {}

#if BUILDFLAG(IS_MAC)
// List of actionable items in the correct order as they appear in the menu.
// If a new button is added to the menu, it should also be added to this list.
// Unfortunately by how Click Tests work we can't verify how many other profile
// buttons are present, so this test merely verifies that at least one exists.
constexpr ProfileMenuViewBase::ActionableItem kActionableItems_RegularWebApp[] =
    {ProfileMenuViewBase::ActionableItem::kOtherProfileButton};
PROFILE_MENU_CLICK_TEST_F(ProfileMenuClickTestWebApp,
                          kActionableItems_RegularWebApp,
                          ProfileMenuClickTest_RegularWebApp) {
  // Add an additional profile.
  Profile* profile1 = GetProfile();
  Profile* profile2 = CreateAdditionalProfile();

  // Install and launch an application in profile1 and also install the same
  // app in profile2.
  webapps::AppId app_id = toolbar_helper().InstallAndLaunchWebApp(
      profile1, GURL("https://test.org"));
  SetTargetBrowser(toolbar_helper().app_browser());
  EXPECT_EQ(app_id,
            toolbar_helper().InstallWebApp(profile2, GURL("https://test.org")));

  RunTest();
}
#endif

class ProfileMenuViewWebAppTest : public ProfileMenuViewTestBase,
                                  public web_app::WebAppBrowserTestBase {};

IN_PROC_BROWSER_TEST_F(ProfileMenuViewWebAppTest,
                       SelectingOtherProfilePasswordManager) {}

#if BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(ProfileMenuViewWebAppTest, SelectingOtherProfile) {
  // Add additional profiles.
  Profile* profile1 = profile();
  Profile* profile2 = CreateAdditionalProfile();
  Profile* profile3 = CreateAdditionalProfile();

  // Install an application in first and third profiles, launching only in the
  // first profile.
  webapps::AppId app_id = toolbar_helper().InstallAndLaunchWebApp(
      profile1, GURL("https://test.org"));
  EXPECT_EQ(app_id,
            toolbar_helper().InstallWebApp(profile3, GURL("https://test.org")));
  SetTargetBrowser(toolbar_helper().app_browser());
  EXPECT_FALSE(chrome::FindBrowserWithProfile(profile3));

  // Open profile menu in first profile.
  auto* toolbar =
      toolbar_helper().browser_view()->web_app_frame_toolbar_for_testing();
  ASSERT_TRUE(toolbar);
  OpenProfileMenuFromToolbar(toolbar);

  // Select third profile by advancing the focus one step forward.
  profile_menu_view()->GetFocusManager()->AdvanceFocus(/*reverse=*/false);
  auto* focused_item = profile_menu_view()->GetFocusManager()->GetFocusedView();
  ASSERT_TRUE(focused_item);

  // Wait for the new app window to be open for the third profile.
  ui_test_utils::AllBrowserTabAddedWaiter waiter;
  Click(focused_item);
  content::WebContents* new_web_contents = waiter.Wait();
  ASSERT_TRUE(new_web_contents);
  EXPECT_FALSE(chrome::FindBrowserWithProfile(profile2));
  Browser* new_browser = chrome::FindBrowserWithProfile(profile3);
  ASSERT_TRUE(new_browser);
  EXPECT_TRUE(new_browser->is_type_app());
  EXPECT_EQ(new_browser->tab_strip_model()->GetActiveWebContents(),
            new_web_contents);
  EXPECT_EQ(new_web_contents->GetVisibleURL(), GURL("https://test.org"));
}

IN_PROC_BROWSER_TEST_F(ProfileMenuViewWebAppTest, ProfileMenuVisibility) {
  // Add an additional profile.
  Profile* profile1 = profile();
  Profile* profile2 = CreateAdditionalProfile();

  // Install and launch an application in first profile.
  webapps::AppId app_id = toolbar_helper().InstallAndLaunchWebApp(
      profile1, GURL("https://test.org"));

  // Verify that avatar button is not visible.
  auto* toolbar_profile1 =
      toolbar_helper().browser_view()->web_app_frame_toolbar_for_testing();
  ASSERT_TRUE(toolbar_profile1);
  ASSERT_TRUE(toolbar_profile1->GetAvatarToolbarButton());
  EXPECT_FALSE(toolbar_profile1->GetAvatarToolbarButton()->GetVisible());

  // Now install and launch application in second profile.
  EXPECT_EQ(app_id, toolbar_helper().InstallAndLaunchWebApp(
                        profile2, GURL("https://test.org")));

  // Avatar button should be visible in both profiles.
  EXPECT_TRUE(toolbar_profile1->GetAvatarToolbarButton()->GetVisible());
  auto* toolbar_profile2 =
      toolbar_helper().browser_view()->web_app_frame_toolbar_for_testing();
  ASSERT_TRUE(toolbar_profile2);
  ASSERT_TRUE(toolbar_profile2->GetAvatarToolbarButton());
  EXPECT_TRUE(toolbar_profile2->GetAvatarToolbarButton()->GetVisible());
}
#endif

#endif  // !BUILDFLAG(IS_CHROMEOS)