chromium/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc

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

#include <string>

#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "chrome/browser/apps/link_capturing/link_capturing_feature_test_support.h"
#include "chrome/browser/apps/link_capturing/link_capturing_features.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/intent_picker_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
#include "chrome/browser/ui/views/intent_picker_bubble_view.h"
#include "chrome/browser/ui/views/location_bar/intent_chip_button.h"
#include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
#include "chrome/browser/ui/views/web_apps/web_app_link_capturing_test_utils.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/test/web_app_navigation_browsertest.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/fenced_frame_test_util.h"
#include "content/public/test/prerender_test_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "ui/views/test/button_test_api.h"
#include "ui/views/widget/any_widget_observer.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/intent_helper/preferred_apps_test_util.h"
#include "components/services/app_service/public/cpp/preferred_apps_list_handle.h"
#include "ui/views/controls/button/checkbox.h"
#include "ui/views/view_utils.h"
#endif

class IntentPickerBrowserTest : public web_app::WebAppNavigationBrowserTest {};

// Tests to do with the behavior of the intent picker icon in the omnibox. Does
// not test the behavior of the intent picker bubble itself.
// Note that behavior specific to the chip version of the icon is tested
// separately in intent_chip_button_browsertest.cc.
class IntentPickerIconBrowserTest
    : public IntentPickerBrowserTest,
      public ::testing::WithParamInterface<std::tuple<std::string, bool>> {};

// Tests that clicking a link from a tabbed browser to outside the scope of an
// installed app does not show the intent picker.
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       NavigationToOutOfScopeLinkDoesNotShowIntentPicker) {}

// Tests that clicking a link from a tabbed browser to within the scope of an
// installed app shows the intent picker icon in Omnibox.
// TODO(crbug.com/41488032): Flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_NavigationToInScopeLinkShowsIntentPicker
#else
#define MAYBE_NavigationToInScopeLinkShowsIntentPicker
#endif
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_NavigationToInScopeLinkShowsIntentPicker) {}

// TODO(crbug.com/41488032): This test is flaky on Mac.
#if BUILDFLAG(IS_MAC)
#define MAYBE_IconVisibilityAfterTabSwitching
#else
#define MAYBE_IconVisibilityAfterTabSwitching
#endif
// Tests that the intent icon updates its visibility when switching between
// tabs.
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_IconVisibilityAfterTabSwitching) {}

// Tests that the navigation in iframe doesn't affect intent picker icon
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       IframeNavigationDoesNotAffectIntentPicker) {}

// Tests that the intent picker icon is not visible if the navigation redirects
// to a URL that doesn't have an installed PWA.
// TODO(crbug.com/41488032): This test is flaky. Re-enable this test.
#if BUILDFLAG(IS_MAC)
#define MAYBE_DoesNotShowIntentPickerWhenRedirectedOutOfScope
#else
#define MAYBE_DoesNotShowIntentPickerWhenRedirectedOutOfScope
#endif
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_DoesNotShowIntentPickerWhenRedirectedOutOfScope) {}

// Test that navigating to service pages (chrome://) will hide the intent picker
// icon.
// TODO(crbug.com/41488032): Re-enable this test
#if BUILDFLAG(IS_MAC)
#define MAYBE_DoNotShowIconAndBubbleOnServicePages
#else
#define MAYBE_DoNotShowIconAndBubbleOnServicePages
#endif
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_DoNotShowIconAndBubbleOnServicePages) {}

// Test that error pages do not show the intent picker icon.
// TODO(crbug.com/41488032): Fix the test.
#if BUILDFLAG(IS_MAC)
#define MAYBE_DoNotShowIconOnErrorPages
#else
#define MAYBE_DoNotShowIconOnErrorPages
#endif  // BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_DoNotShowIconOnErrorPages) {}

// Test that loading a page with pushState() call that changes URL updates the
// intent picker view.
// TODO(crbug.com/41488032): Re-enable this test
#if BUILDFLAG(IS_MAC)
#define MAYBE_PushStateURLChangeTest
#else
#define MAYBE_PushStateURLChangeTest
#endif
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserTest,
                       MAYBE_PushStateURLChangeTest) {}

INSTANTIATE_TEST_SUITE_P();

class IntentPickerIconBrowserBubbleTest
    : public IntentPickerBrowserTest,
      public testing::WithParamInterface<bool> {};

#if BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserBubbleTest,
                       IntentChipOpensBubble) {
  InstallTestWebApp();
  const GURL in_scope_url =
      https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());

  views::Button* intent_picker_icon = GetIntentPickerIcon();

  OpenNewTab(in_scope_url);
  EXPECT_TRUE(intent_picker_icon->GetVisible());

  ASSERT_TRUE(web_app::ClickIntentPickerAndWaitForBubble(browser()));

  EXPECT_EQ(1U, GetItemContainerSize(intent_picker_bubble()));
  auto& app_info = intent_picker_bubble()->app_info_for_testing();
  ASSERT_EQ(1U, app_info.size());
  EXPECT_EQ(test_web_app_id(), app_info[0].launch_name);
  EXPECT_EQ(GetAppName(), app_info[0].display_name);
}

// Test that the "Remember this choice" checkbox works.
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserBubbleTest, RememberOpenWebApp) {
  base::HistogramTester histogram_tester;

  InstallTestWebApp();
  const GURL in_scope_url =
      https_server().GetURL(GetAppUrlHost(), GetInScopeUrlPath());

  OpenNewTab(in_scope_url);
  ASSERT_TRUE(web_app::ClickIntentPickerAndWaitForBubble(browser()));

  // Check "Remember my choice" and accept the bubble.
  views::Checkbox* remember_selection_checkbox =
      views::AsViewClass<views::Checkbox>(intent_picker_bubble()->GetViewByID(
          IntentPickerBubbleView::ViewId::kRememberCheckbox));
  ASSERT_TRUE(remember_selection_checkbox);
  ASSERT_TRUE(remember_selection_checkbox->GetEnabled());
  remember_selection_checkbox->SetChecked(true);

  apps::PreferredAppsListHandle& preferred_apps =
      apps::AppServiceProxyFactory::GetForProfile(profile())
          ->PreferredAppsList();
  apps_util::PreferredAppUpdateWaiter preference_update_waiter(
      preferred_apps, test_web_app_id());

  ui_test_utils::BrowserChangeObserver added_observer(
      nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
  intent_picker_bubble()->AcceptDialog();

  // Accepting the bubble should open the app.
  Browser* app_browser = added_observer.Wait();
  ASSERT_TRUE(web_app::AppBrowserController::IsForWebApp(app_browser,
                                                         test_web_app_id()));

  // The link capturing preference should be updated.
  preference_update_waiter.Wait();
  ASSERT_TRUE(
      preferred_apps.IsPreferredAppForSupportedLinks(test_web_app_id()));

  // Check that we recorded that settings were changed.
  histogram_tester.ExpectBucketCount(
      "ChromeOS.Intents.LinkCapturingEvent2.WebApp",
      apps::IntentHandlingMetrics::LinkCapturingEvent::kSettingsChanged, 1);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.Intents.LinkCapturingEvent2.ArcApp",
      apps::IntentHandlingMetrics::LinkCapturingEvent::kSettingsChanged, 0);
  histogram_tester.ExpectBucketCount(
      "ChromeOS.Intents.LinkCapturingEvent2",
      apps::IntentHandlingMetrics::LinkCapturingEvent::kSettingsChanged, 1);
}

#else
IN_PROC_BROWSER_TEST_P(IntentPickerIconBrowserBubbleTest,
                       DISABLED_IntentChipLaunchesAppDirectly) {}
#endif  // BUILDFLAG(IS_CHROMEOS)

INSTANTIATE_TEST_SUITE_P();

// This test only works when link capturing is set to default off for desktop
// platforms, as prerendering navigations are aborted during link captured app
// launches. See LinkCapturingNavigationThrottle::MaybeCreate for more
// information.
// TODO(b/297256243): Investigate prerendering integration with link capturing.
class IntentPickerIconPrerenderingBrowserTest
    : public IntentPickerIconBrowserTest {};

IN_PROC_BROWSER_TEST_P(IntentPickerIconPrerenderingBrowserTest,
                       PrerenderingShouldNotShowIntentPicker) {}

INSTANTIATE_TEST_SUITE_P();

class IntentPickerIconFencedFrameBrowserTest
    : public IntentPickerIconBrowserTest {};

IN_PROC_BROWSER_TEST_P(IntentPickerIconFencedFrameBrowserTest,
                       ShouldShowIntentPickerInFencedFrame) {}

INSTANTIATE_TEST_SUITE_P();