blob: df90e9c26b5abcdfcf8e330f25c61f1976349900 [file] [log] [blame]
// Copyright 2024 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 "ash/capture_mode/capture_mode_controller.h"
#include "ash/capture_mode/capture_mode_test_util.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/search_results_panel.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/ash_web_view.h"
#include "ash/public/cpp/capture_mode/capture_mode_api.h"
#include "base/functional/callback_helpers.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h"
#include "chrome/browser/ui/ash/capture_mode/search_results_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/lens/lens_overlay_permission_utils.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/common/referrer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_navigation_observer.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"
namespace {
constexpr char kSearchResultClickedHistogram[] =
"Ash.CaptureModeController.SearchResultClicked.ClamshellMode";
std::unique_ptr<views::Widget> CreateWidget() {
views::Widget::InitParams params(
views::Widget::InitParams::CLIENT_OWNS_WIDGET,
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
return std::make_unique<views::Widget>(std::move(params));
}
// Given an embedded script `body`, wraps it in a basic HTML structure, then
// returns a GURL accessible by the local test server.
GURL CreateDataUrlWithBody(const std::string& body) {
return GURL(base::StringPrintf(R"(data:text/html,
<html>
<body>
<style>
* {
margin: 0;
padding: 0;
}
</style>
%s
</body>
</html>
)",
body.c_str()));
}
} // namespace
namespace ash {
class SunfishBrowserTest : public InProcessBrowserTest {
public:
SunfishBrowserTest() = default;
SunfishBrowserTest(const SunfishBrowserTest&) = delete;
SunfishBrowserTest& operator=(const SunfishBrowserTest&) = delete;
~SunfishBrowserTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_{features::kSunfishFeature};
};
// Tests the basic functionalities of `SearchResultsView`.
IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, SearchResultsView) {
auto widget = CreateWidget();
ChromeCaptureModeDelegate* delegate = ChromeCaptureModeDelegate::Get();
auto* contents_view =
widget->SetContentsView(delegate->CreateSearchResultsView());
auto* search_results_view =
views::AsViewClass<SearchResultsView>(contents_view);
ASSERT_TRUE(search_results_view);
}
// Tests that links are opened in new tabs.
IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, OpenLinksInNewTabs) {
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kSearchResultClickedHistogram, 0);
auto* controller = CaptureModeController::Get();
controller->StartSunfishSession();
VerifyActiveBehavior(BehaviorType::kSunfish);
// Simulate showing the panel while the session is active.
controller->ShowSearchResultsPanel();
controller->NavigateSearchResultsPanel(GURL("kTestUrl1"));
ASSERT_TRUE(controller->IsActive());
auto* search_results_view =
controller->GetSearchResultsPanel()->search_results_view();
// Browser tests start out with 1 browser tab by default.
EXPECT_EQ(1, browser()->tab_strip_model()->count());
// Simulate clicking on a new URL in the web view.
auto* child_view =
search_results_view->GetViewByID(kAshWebViewChildWebViewId);
ASSERT_TRUE(child_view);
auto* web_view = views::AsViewClass<views::WebView>(child_view);
ASSERT_TRUE(web_view);
auto* web_contents = web_view->web_contents();
web_contents->GetController().LoadURL(
CreateDataUrlWithBody(R"(
<script>
// Wait until window has finished loading.
window.addEventListener("load", () => {
// Perform simple click on an anchor within the same target.
const anchor = document.createElement("a");
anchor.href = "https://google.com/";
anchor.click();
// Wait for first click event to be flushed.
setTimeout(() => {
// Perform simple click on an anchor with "_blank" target.
const anchor = document.createElement("a");
anchor.href = "https://assistant.google.com/";
anchor.target = "_blank";
anchor.click();
}, 0);
});
</script>
)"),
content::Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
content::TestNavigationObserver observer(web_contents);
observer.Wait();
EXPECT_TRUE(observer.last_navigation_succeeded());
// Test it opens a new tab and ends the session.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_FALSE(controller->IsActive());
histogram_tester.ExpectTotalCount(kSearchResultClickedHistogram, 1);
}
// Tests that links are correctly opened in off the record profiles.
IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, OpensLinksOffTheRecord) {
base::HistogramTester histogram_tester;
histogram_tester.ExpectTotalCount(kSearchResultClickedHistogram, 0);
auto* controller = CaptureModeController::Get();
controller->StartSunfishSession();
VerifyActiveBehavior(BehaviorType::kSunfish);
// Simulate showing the panel while the session is active.
controller->ShowSearchResultsPanel();
controller->NavigateSearchResultsPanel(GURL("kTestUrl1"));
ASSERT_TRUE(controller->IsActive());
auto* search_results_view =
controller->GetSearchResultsPanel()->search_results_view();
// Simulate clicking on a new URL in the web view.
views::View* child_view =
search_results_view->GetViewByID(kAshWebViewChildWebViewId);
ASSERT_TRUE(child_view);
auto* web_view = views::AsViewClass<views::WebView>(child_view);
ASSERT_TRUE(web_view);
content::WebContents* web_contents = web_view->web_contents();
ASSERT_TRUE(web_contents);
content::OpenURLParams params(
GURL("https://assistant.google.com"), content::Referrer(),
WindowOpenDisposition::OFF_THE_RECORD, ui::PAGE_TRANSITION_LINK,
/*is_renderer_initiated=*/false);
content::WebContents* new_contents =
web_contents->OpenURL(params,
/*navigation_handle_callback=*/base::DoNothing());
Profile* profile =
Profile::FromBrowserContext(new_contents->GetBrowserContext());
EXPECT_TRUE(profile->IsOffTheRecord());
histogram_tester.ExpectTotalCount(kSearchResultClickedHistogram, 1);
}
IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, SendSearchRequests) {
// Send a region search, simulated inside a regular capture session.
ChromeCaptureModeDelegate* delegate = ChromeCaptureModeDelegate::Get();
delegate->SendLensWebRegionSearch(gfx::Image(),
/*is_standalone_session=*/false,
base::BindRepeating([](GURL url) {}),
base::DoNothing(), base::DoNothing());
// Send a region search, simulated inside a standalone Sunfish session.
delegate->SendLensWebRegionSearch(gfx::Image(),
/*is_standalone_session=*/true,
base::BindRepeating([](GURL url) {}),
base::DoNothing(), base::DoNothing());
}
// Tests that the policy can be read and set browser-side.
IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, BrowserPolicy) {
ChromeCaptureModeDelegate* delegate = ChromeCaptureModeDelegate::Get();
EXPECT_TRUE(delegate->IsSearchAllowedByPolicy());
EXPECT_TRUE(CanShowSunfishUi());
browser()->profile()->GetPrefs()->SetInteger(
lens::prefs::kLensOverlaySettings,
static_cast<int>(lens::prefs::LensOverlaySettingsPolicyValue::kDisabled));
EXPECT_FALSE(delegate->IsSearchAllowedByPolicy());
EXPECT_FALSE(CanShowSunfishUi());
}
} // namespace ash