| // 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. |
| |
| #include <optional> |
| #include <string> |
| |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/test/bind.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/media/router/media_router_feature.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/accelerator_utils.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_element_identifiers.h" |
| #include "chrome/browser/ui/browser_list.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/toolbar/app_menu_model.h" |
| #include "chrome/browser/ui/ui_features.h" |
| #include "chrome/browser/web_applications/test/web_app_icon_test_utils.h" |
| #include "chrome/browser/web_applications/test/web_app_test_utils.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/extensions/api/dashboard_private.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chrome/test/base/interactive_test_utils.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chrome/test/interaction/interactive_browser_test.h" |
| #include "chrome/test/interaction/tracked_element_webcontents.h" |
| #include "chrome/test/interaction/webcontents_interaction_test_util.h" |
| #include "components/password_manager/core/common/password_manager_features.h" |
| #include "components/performance_manager/public/features.h" |
| #include "components/webapps/browser/banners/app_banner_manager.h" |
| #include "components/webapps/browser/banners/installable_web_app_check_result.h" |
| #include "components/webapps/browser/banners/web_app_banner_data.h" |
| #include "components/webapps/browser/installable/installable_metrics.h" |
| #include "content/public/test/browser_test.h" |
| #include "extensions/common/extension_features.h" |
| #include "extensions/common/extension_urls.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/accelerators/menu_label_accelerator_util.h" |
| #include "ui/base/interaction/element_identifier.h" |
| #include "ui/base/interaction/element_tracker.h" |
| #include "ui/base/interaction/expect_call_in_scope.h" |
| #include "ui/base/interaction/interaction_sequence.h" |
| #include "ui/base/interaction/state_observer.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/simple_menu_model.h" |
| #include "ui/base/ui_base_features.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| #include "ui/views/controls/menu/menu_item_view.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPrimaryTabPageElementId); |
| } // namespace |
| |
| class AppMenuModelInteractiveTest : public InteractiveBrowserTest { |
| public: |
| AppMenuModelInteractiveTest() = default; |
| ~AppMenuModelInteractiveTest() override = default; |
| AppMenuModelInteractiveTest(const AppMenuModelInteractiveTest&) = delete; |
| void operator=(const AppMenuModelInteractiveTest&) = delete; |
| |
| void SetUp() override { |
| set_open_about_blank_on_browser_launch(true); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| InteractiveBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| InteractiveBrowserTest::SetUpOnMainThread(); |
| embedded_test_server()->StartAcceptingConnections(); |
| } |
| |
| void TearDownOnMainThread() override { |
| EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| InteractiveBrowserTest::TearDownOnMainThread(); |
| } |
| |
| protected: |
| auto CheckIncognitoWindowOpened(const Browser* default_browser) { |
| return Check(base::BindLambdaForTesting([default_browser]() { |
| Browser* new_browser = nullptr; |
| if (BrowserList::GetIncognitoBrowserCount() == 1) { |
| EXPECT_EQ(2u, BrowserList::GetInstance()->size()); |
| for (Browser* browser : *BrowserList::GetInstance()) { |
| if (browser != default_browser) { |
| new_browser = browser; |
| break; |
| } |
| } |
| CHECK(new_browser); |
| } else { |
| new_browser = ui_test_utils::WaitForBrowserToOpen(); |
| } |
| return new_browser->profile()->IsIncognitoProfile(); |
| })); |
| } |
| |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(AppMenuModelInteractiveTest, PerformanceNavigation) { |
| RunTestSequence(InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| SelectMenuItem(AppMenuModel::kMoreToolsMenuItem), |
| SelectMenuItem(ToolsMenuModel::kPerformanceMenuItem), |
| WaitForWebContentsNavigation( |
| kPrimaryTabPageElementId, |
| GURL(chrome::kChromeUIPerformanceSettingsURL))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AppMenuModelInteractiveTest, IncognitoMenuItem) { |
| RunTestSequence(PressButton(kToolbarAppMenuButtonElementId), |
| SelectMenuItem(AppMenuModel::kIncognitoMenuItem), |
| CheckIncognitoWindowOpened(browser())); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AppMenuModelInteractiveTest, IncognitoAccelerator) { |
| ui::Accelerator incognito_accelerator; |
| chrome::AcceleratorProviderForBrowser(browser())->GetAcceleratorForCommandId( |
| IDC_NEW_INCOGNITO_WINDOW, &incognito_accelerator); |
| |
| RunTestSequence( |
| SendAccelerator(kToolbarAppMenuButtonElementId, incognito_accelerator), |
| CheckIncognitoWindowOpened(browser())); |
| } |
| |
| class ExtensionsMenuModelInteractiveTest : public AppMenuModelInteractiveTest { |
| public: |
| explicit ExtensionsMenuModelInteractiveTest(bool enable_feature = true) { |
| std::vector<base::test::FeatureRef> enabled_features; |
| std::vector<base::test::FeatureRef> disabled_features; |
| if (enable_feature) { |
| enabled_features = {features::kExtensionsMenuInAppMenu}; |
| disabled_features = {features::kChromeRefresh2023}; |
| } else { |
| enabled_features = {}; |
| disabled_features = {features::kExtensionsMenuInAppMenu, |
| features::kChromeRefresh2023}; |
| } |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| ~ExtensionsMenuModelInteractiveTest() override = default; |
| ExtensionsMenuModelInteractiveTest( |
| const ExtensionsMenuModelInteractiveTest&) = delete; |
| void operator=(const ExtensionsMenuModelInteractiveTest&) = delete; |
| |
| void SetUp() override { |
| set_open_about_blank_on_browser_launch(true); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| InteractiveBrowserTest::SetUp(); |
| } |
| |
| protected: |
| base::HistogramTester histograms; |
| }; |
| |
| class ExtensionsMenuModelPresenceTest |
| : public ExtensionsMenuModelInteractiveTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| ExtensionsMenuModelPresenceTest() |
| : ExtensionsMenuModelInteractiveTest(/*enable_feature=*/GetParam()) {} |
| ~ExtensionsMenuModelPresenceTest() override = default; |
| ExtensionsMenuModelPresenceTest(const ExtensionsMenuModelPresenceTest&) = |
| delete; |
| void operator=(const ExtensionsMenuModelPresenceTest&) = delete; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ExtensionsMenuModelPresenceTest, |
| testing::Bool(), |
| [](const testing::TestParamInfo<ExtensionsMenuModelPresenceTest::ParamType>& |
| info) { return info.param ? "InRootAppMenu" : "NotInRootAppMenu"; }); |
| |
| // Test to confirm that the structure of the Extensions menu is present but that |
| // no histograms are logged since it isn't interacted with. |
| IN_PROC_BROWSER_TEST_P(ExtensionsMenuModelPresenceTest, MenuPresence) { |
| if (features::IsExtensionMenuInRootAppMenu()) { // Menu enabled |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kExtensionsMenuItem), |
| SelectMenuItem(AppMenuModel::kExtensionsMenuItem), |
| EnsurePresent(ExtensionsMenuModel::kManageExtensionsMenuItem), |
| EnsurePresent(ExtensionsMenuModel::kVisitChromeWebStoreMenuItem)); |
| } else { |
| RunTestSequence(InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsureNotPresent(AppMenuModel::kExtensionsMenuItem)); |
| } |
| |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 0); |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 0); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_MANAGE_EXTENSIONS, 0); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_VISIT_CHROME_WEB_STORE, 0); |
| } |
| |
| // Test to confirm that the manage extensions menu item navigates when selected |
| // and emite histograms that it did so. |
| IN_PROC_BROWSER_TEST_F(ExtensionsMenuModelInteractiveTest, ManageExtensions) { |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| SelectMenuItem(AppMenuModel::kExtensionsMenuItem), |
| SelectMenuItem(ExtensionsMenuModel::kManageExtensionsMenuItem), |
| WaitForWebContentsNavigation(kPrimaryTabPageElementId, |
| GURL(chrome::kChromeUIExtensionsURL))); |
| |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 1); |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 0); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_MANAGE_EXTENSIONS, 1); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_VISIT_CHROME_WEB_STORE, 0); |
| } |
| |
| // TODO(crbug.com/1488136): Remove this test in favor of a unit test |
| // extension_urls::GetWebstoreLaunchURL(). |
| class ExtensionsMenuVisitChromeWebstoreModelInteractiveTest |
| : public AppMenuModelInteractiveTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| ExtensionsMenuVisitChromeWebstoreModelInteractiveTest() { |
| std::vector<base::test::FeatureRef> enabled_features = { |
| features::kExtensionsMenuInAppMenu}; |
| std::vector<base::test::FeatureRef> disabled_features{}; |
| if (GetParam()) { |
| enabled_features.push_back(extensions_features::kNewWebstoreURL); |
| } else { |
| LOG(ERROR) << "disabling new webstore URL"; |
| disabled_features.push_back(extensions_features::kNewWebstoreURL); |
| } |
| scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); |
| } |
| |
| void SetUp() override { |
| set_open_about_blank_on_browser_launch(true); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| InteractiveBrowserTest::SetUp(); |
| } |
| |
| protected: |
| base::HistogramTester histograms; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| ExtensionsMenuVisitChromeWebstoreModelInteractiveTest, |
| // extensions_features::kNewWebstoreURL enabled status. |
| testing::Bool(), |
| [](const testing::TestParamInfo<ExtensionsMenuModelPresenceTest::ParamType>& |
| info) { |
| return info.param ? "NewVisitChromeWebstoreUrl" |
| : "OldVisitChromeWebstoreUrl"; |
| }); |
| |
| // Test to confirm that the visit Chrome Web Store menu item navigates to the |
| // correct chrome webstore URL when selected and emits histograms that it did |
| // so. |
| IN_PROC_BROWSER_TEST_P(ExtensionsMenuVisitChromeWebstoreModelInteractiveTest, |
| VisitChromeWebStore) { |
| GURL expected_webstore_launch_url = |
| GetParam() ? extension_urls::GetNewWebstoreLaunchURL() |
| : extension_urls::GetWebstoreLaunchURL(); |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| SelectMenuItem(AppMenuModel::kExtensionsMenuItem), |
| SelectMenuItem(ExtensionsMenuModel::kVisitChromeWebStoreMenuItem), |
| WaitForWebContentsNavigation( |
| kPrimaryTabPageElementId, |
| extension_urls::AppendUtmSource(expected_webstore_launch_url, |
| extension_urls::kAppMenuUtmSource))); |
| |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.VisitChromeWebStore", 1); |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.ManageExtensions", 0); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_VISIT_CHROME_WEB_STORE, 1); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_MANAGE_EXTENSIONS, 0); |
| } |
| |
| class PasswordManagerMenuItemInteractiveTest |
| : public AppMenuModelInteractiveTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| PasswordManagerMenuItemInteractiveTest() { |
| scoped_feature_list_.InitWithFeatureStates( |
| {{features::kChromeRefresh2023, false}, |
| {features::kChromeRefreshSecondary2023, false}}); |
| } |
| PasswordManagerMenuItemInteractiveTest( |
| const PasswordManagerMenuItemInteractiveTest&) = delete; |
| void operator=(const PasswordManagerMenuItemInteractiveTest&) = delete; |
| |
| ~PasswordManagerMenuItemInteractiveTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerMenuItemInteractiveTest, |
| PasswordManagerMenuItem) { |
| base::HistogramTester histograms; |
| |
| RunTestSequence(InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| SelectMenuItem(AppMenuModel::kPasswordManagerMenuItem), |
| WaitForWebContentsNavigation( |
| kPrimaryTabPageElementId, |
| GURL("chrome://password-manager/passwords"))); |
| |
| histograms.ExpectTotalCount("WrenchMenu.TimeToAction.PasswordManager", 1); |
| histograms.ExpectBucketCount("WrenchMenu.MenuAction", |
| MENU_ACTION_PASSWORD_MANAGER, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PasswordManagerMenuItemInteractiveTest, |
| NoMenuItemOnPasswordManagerPage) { |
| RunTestSequence( |
| AddInstrumentedTab(kPrimaryTabPageElementId, |
| GURL("chrome://password-manager/passwords")), |
| WaitForWebContentsReady(kPrimaryTabPageElementId, |
| GURL("chrome://password-manager/passwords")), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsureNotPresent(AppMenuModel::kPasswordManagerMenuItem)); |
| } |
| |
| class CastExperimentAppMenuModelInteractiveTest |
| : public AppMenuModelInteractiveTest { |
| public: |
| CastExperimentAppMenuModelInteractiveTest() { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/ |
| {{features::kCastAppMenuExperiment, |
| {{features::kCastListedFirst.name, "false"}}}, |
| {features::kChromeRefresh2023, {}}}, |
| /*disabled_features=*/{}); |
| } |
| CastExperimentAppMenuModelInteractiveTest( |
| const CastExperimentAppMenuModelInteractiveTest&) = delete; |
| void operator=(const CastExperimentAppMenuModelInteractiveTest&) = delete; |
| |
| ~CastExperimentAppMenuModelInteractiveTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(CastExperimentAppMenuModelInteractiveTest, |
| SaveShareCastSubMenuItemText) { |
| if (!media_router::MediaRouterEnabled(browser()->profile())) { |
| GTEST_SKIP() |
| << "The cast experiment tested here only exists if cast is enabled."; |
| } |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kSaveAndShareMenuItem), |
| CheckViewProperty( |
| AppMenuModel::kSaveAndShareMenuItem, &views::MenuItemView::title, |
| l10n_util::GetStringUTF16(IDS_SAVE_SHARE_AND_CAST_MENU))); |
| } |
| |
| class CastListedFirstExperimentAppMenuModelInteractiveTest |
| : public AppMenuModelInteractiveTest { |
| public: |
| CastListedFirstExperimentAppMenuModelInteractiveTest() { |
| scoped_feature_list_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/ |
| {{features::kCastAppMenuExperiment, |
| {{features::kCastListedFirst.name, "true"}}}, |
| {features::kChromeRefresh2023, {}}}, |
| /*disabled_features=*/{}); |
| } |
| CastListedFirstExperimentAppMenuModelInteractiveTest( |
| const CastListedFirstExperimentAppMenuModelInteractiveTest&) = delete; |
| void operator=(const CastListedFirstExperimentAppMenuModelInteractiveTest&) = |
| delete; |
| |
| ~CastListedFirstExperimentAppMenuModelInteractiveTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(CastListedFirstExperimentAppMenuModelInteractiveTest, |
| CastSaveShareSubMenuItemText) { |
| if (!media_router::MediaRouterEnabled(browser()->profile())) { |
| GTEST_SKIP() |
| << "The cast experiment tested here only exists if cast is enabled."; |
| } |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kSaveAndShareMenuItem), |
| CheckViewProperty( |
| AppMenuModel::kSaveAndShareMenuItem, &views::MenuItemView::title, |
| l10n_util::GetStringUTF16(IDS_CAST_SAVE_AND_SHARE_MENU)), |
| SelectMenuItem(AppMenuModel::kSaveAndShareMenuItem), |
| EnsurePresent(AppMenuModel::kCastTitleItem)); |
| } |
| |
| using ui::test::ObservationStateObserver; |
| using webapps::AppBannerManager; |
| using webapps::InstallableWebAppCheckResult; |
| using webapps::WebAppBannerData; |
| |
| class AppBannerManagerInstallStateObserver |
| : public ObservationStateObserver<InstallableWebAppCheckResult, |
| AppBannerManager, |
| AppBannerManager::Observer> { |
| public: |
| explicit AppBannerManagerInstallStateObserver( |
| AppBannerManager* app_banner_manager) |
| : ObservationStateObserver(app_banner_manager) {} |
| ~AppBannerManagerInstallStateObserver() override = default; |
| |
| // ObservationStateObserver: |
| InstallableWebAppCheckResult GetStateObserverInitialState() const override { |
| return source()->GetInstallableWebAppCheckResult(); |
| } |
| |
| // AppBannerManager::Observer: |
| void OnInstallableWebAppStatusUpdated( |
| InstallableWebAppCheckResult result, |
| const std::optional<WebAppBannerData>& data) override { |
| OnStateObserverStateChanged(result); |
| } |
| }; |
| |
| namespace { |
| DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(AppBannerManagerInstallStateObserver, |
| kAppBannerManagerState); |
| } // namespace |
| |
| class UniversalInstallAppMenuModelInteractiveTest |
| : public AppMenuModelInteractiveTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| UniversalInstallAppMenuModelInteractiveTest() { |
| if (GetParam()) { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kWebAppUniversalInstall); |
| } else { |
| scoped_feature_list_.InitAndDisableFeature( |
| features::kWebAppUniversalInstall); |
| } |
| } |
| UniversalInstallAppMenuModelInteractiveTest( |
| const UniversalInstallAppMenuModelInteractiveTest&) = delete; |
| void operator=(const UniversalInstallAppMenuModelInteractiveTest&) = delete; |
| ~UniversalInstallAppMenuModelInteractiveTest() override = default; |
| |
| protected: |
| GURL GetNonInstallableAppUrl() { |
| return embedded_test_server()->GetURL( |
| "/banners/no_manifest_test_page.html"); |
| } |
| |
| GURL GetInstallableAppUrl() { |
| return embedded_test_server()->GetURL("/banners/manifest_test_page.html"); |
| } |
| |
| // If universal install is enabled, non installable sites (DIY apps) will have |
| // a corresponding menu item entry for installation, as well as the default |
| // install icon next to them. |
| auto VerifyDiyAppMenuItemViews() { |
| if (IsUniversalInstallEnabled()) { |
| const ui::ImageModel icon_image = ui::ImageModel::FromVectorIcon( |
| kInstallDesktopChromeRefreshIcon, ui::kColorMenuIcon, |
| ui::SimpleMenuModel::kDefaultIconSize); |
| return Steps( |
| EnsurePresent(AppMenuModel::kInstallAppItem), |
| CheckViewProperty( |
| AppMenuModel::kInstallAppItem, &views::MenuItemView::title, |
| l10n_util::GetStringUTF16(IDS_INSTALL_DIY_TO_OS_LAUNCH_SURFACE)), |
| CheckViewProperty(AppMenuModel::kInstallAppItem, |
| &views::MenuItemView::GetIcon, icon_image)); |
| } else { |
| return Steps(EnsureNotPresent(AppMenuModel::kInstallAppItem)); |
| } |
| } |
| |
| AppBannerManager* GetManager() { |
| return AppBannerManager::FromWebContents( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| } |
| |
| // If universal install is enabled, the app icon should be shown in the menu |
| // entry, so compare the middle most color as an indicator of similarity (as |
| // is the case for most web_applications/ based tests). For the other case, |
| // the default chrome refresh icon is a vector icon that is easy to compare, |
| // so we do a 1:1 comparison. |
| auto CompareIcons() { |
| return base::BindLambdaForTesting([&](views::MenuItemView* item_view) { |
| if (IsUniversalInstallEnabled()) { |
| EXPECT_TRUE(item_view->GetIcon().IsImage()); |
| EXPECT_EQ( |
| GetMidColorFromBitmap(item_view->GetIcon().GetImage().AsBitmap()), |
| GetAppIconColorBasedOnBannerData()); |
| } else { |
| EXPECT_EQ(item_view->GetIcon(), |
| ui::ImageModel::FromVectorIcon( |
| kInstallDesktopChromeRefreshIcon, ui::kColorMenuIcon, |
| ui::SimpleMenuModel::kDefaultIconSize)); |
| } |
| }); |
| } |
| |
| bool InstallNonLocallyInstalledApp(const GURL& url) { |
| auto install_info = |
| web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(url); |
| install_info->title = u"Test App"; |
| install_info->user_display_mode = |
| web_app::mojom::UserDisplayMode::kStandalone; |
| web_app::WebAppInstallParams params; |
| params.locally_installed = false; |
| params.add_to_applications_menu = false; |
| params.add_to_desktop = false; |
| params.add_to_quick_launch_bar = false; |
| params.add_to_search = false; |
| base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode> |
| result; |
| auto* provider = web_app::WebAppProvider::GetForTest(browser()->profile()); |
| provider->scheduler().InstallFromInfoWithParams( |
| std::move(install_info), /*overwrite_existing_manifest_fields=*/true, |
| webapps::WebappInstallSource::SYNC, result.GetCallback(), params); |
| bool success = result.Wait(); |
| const webapps::AppId& app_id = result.Get<webapps::AppId>(); |
| EXPECT_FALSE(provider->registrar_unsafe().IsLocallyInstalled(app_id)); |
| return success; |
| } |
| |
| private: |
| bool IsUniversalInstallEnabled() { return GetParam(); } |
| |
| SkColor GetAppIconColorBasedOnBannerData() { |
| std::optional<WebAppBannerData> banner_data = |
| GetManager()->GetCurrentWebAppBannerData(); |
| |
| if (banner_data->primary_icon.empty()) { |
| return SK_ColorTRANSPARENT; |
| } |
| |
| gfx::ImageSkia primary_icon = |
| gfx::ImageSkia::CreateFrom1xBitmap(banner_data->primary_icon); |
| gfx::ImageSkia resized_app_icon = |
| gfx::ImageSkiaOperations::CreateResizedImage( |
| primary_icon, skia::ImageOperations::RESIZE_BEST, |
| gfx::Size(ui::SimpleMenuModel::kDefaultIconSize, |
| ui::SimpleMenuModel::kDefaultIconSize)); |
| |
| return GetMidColorFromBitmap(*resized_app_icon.bitmap()); |
| } |
| |
| SkColor GetMidColorFromBitmap(const SkBitmap& bitmap) { |
| return bitmap.getColor(bitmap.width() / 2, bitmap.height() / 2); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(UniversalInstallAppMenuModelInteractiveTest, |
| DIYAppMenuWorksCorrectly) { |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| ObserveState(kAppBannerManagerState, GetManager()), |
| NavigateWebContents(kPrimaryTabPageElementId, GetNonInstallableAppUrl()), |
| WaitForWebContentsReady(kPrimaryTabPageElementId), |
| WaitForState(kAppBannerManagerState, InstallableWebAppCheckResult::kNo), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kSaveAndShareMenuItem), |
| SelectMenuItem(AppMenuModel::kSaveAndShareMenuItem), |
| VerifyDiyAppMenuItemViews()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UniversalInstallAppMenuModelInteractiveTest, |
| InstallAppMenuWorksCorrectly) { |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| ObserveState(kAppBannerManagerState, GetManager()), |
| NavigateWebContents(kPrimaryTabPageElementId, GetInstallableAppUrl()), |
| WaitForWebContentsReady(kPrimaryTabPageElementId), |
| WaitForState(kAppBannerManagerState, |
| InstallableWebAppCheckResult::kYes_Promotable), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kSaveAndShareMenuItem), |
| SelectMenuItem(AppMenuModel::kSaveAndShareMenuItem), |
| EnsurePresent(AppMenuModel::kInstallAppItem), |
| CheckViewProperty( |
| AppMenuModel::kInstallAppItem, &views::MenuItemView::title, |
| l10n_util::GetStringFUTF16( |
| IDS_INSTALL_TO_OS_LAUNCH_SURFACE, |
| ui::EscapeMenuLabelAmpersands(u"Manifest test app"))), |
| WithView(AppMenuModel::kInstallAppItem, CompareIcons())); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(UniversalInstallAppMenuModelInteractiveTest, |
| InstallAppMenuShowsForNonLocallyInstalledApps) { |
| EXPECT_TRUE(InstallNonLocallyInstalledApp(GetInstallableAppUrl())); |
| RunTestSequence( |
| InstrumentTab(kPrimaryTabPageElementId), |
| ObserveState(kAppBannerManagerState, GetManager()), |
| NavigateWebContents(kPrimaryTabPageElementId, GetInstallableAppUrl()), |
| WaitForWebContentsReady(kPrimaryTabPageElementId), |
| WaitForState(kAppBannerManagerState, |
| InstallableWebAppCheckResult::kYes_Promotable), |
| PressButton(kToolbarAppMenuButtonElementId), |
| EnsurePresent(AppMenuModel::kSaveAndShareMenuItem), |
| SelectMenuItem(AppMenuModel::kSaveAndShareMenuItem), |
| EnsurePresent(AppMenuModel::kInstallAppItem)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| UniversalInstallAppMenuModelInteractiveTest, |
| ::testing::Bool(), |
| [](const testing::TestParamInfo<bool>& info) { |
| return info.param ? "UniversalInstallEnabled" |
| : "UniversalInstallDisabled"; |
| }); |