blob: 7c7d06c02d80d613e30ef9e84f8fef6ae6b8e468 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/omnibox/omnibox_next_features.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_presenter_base.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_view_webui.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_webui_content.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/browser/ui/webui/test_support/webui_interactive_test_mixin.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "components/omnibox/browser/omnibox_field_trial.h"
#include "content/public/test/browser_test.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/interaction_sequence.h"
#include "ui/views/controls/webview/webview.h"
namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kPopupWebView);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTab);
using DeepQuery = WebContentsInteractionTestUtil::DeepQuery;
const DeepQuery kDropdownContent = {"omnibox-popup-app",
"cr-searchbox-dropdown", "#content"};
const DeepQuery kMatch = {"omnibox-popup-app", "cr-searchbox-dropdown",
"cr-searchbox-match"};
const DeepQuery kMatchText = {"omnibox-popup-app", "cr-searchbox-dropdown",
"cr-searchbox-match", "#suggestion"};
} // namespace
class OmniboxWebUiInteractiveTest
: public WebUiInteractiveTestMixin<InteractiveBrowserTest> {
public:
OmniboxWebUiInteractiveTest() {
feature_list_.InitAndEnableFeature(omnibox::kWebUIOmniboxPopup);
}
~OmniboxWebUiInteractiveTest() override = default;
protected:
// Returns the currently visible `OmniboxPopupWebUIContent`. An
// `OmniboxPopupView` may host multiple content views, but only one is
// visible at any given time.
auto GetActivePopupWebView() {
return base::BindLambdaForTesting([&]() -> views::View* {
auto* popup_view = static_cast<OmniboxPopupViewWebUI*>(
BrowserView::GetBrowserViewForBrowser(browser())
->toolbar()
->location_bar()
->GetOmniboxPopupViewForTesting());
return popup_view->presenter_->GetWebUIContent();
});
}
auto WaitForPopupReady() {
return Steps(InAnyContext(WaitForShow(
OmniboxPopupPresenterBase::kRoundedResultsFrame)),
InAnyContext(InstrumentNonTabWebView(kPopupWebView,
GetActivePopupWebView())),
InSameContext(WaitForWebContentsReady(
kPopupWebView, GURL(chrome::kChromeUIOmniboxPopupURL))));
}
// Enters Gemini mode in the omnibox and waits for the popup to be ready.
auto EnterGeminiMode() {
return Steps(FocusElement(kOmniboxElementId),
EnterText(kOmniboxElementId, u"@gemini"),
SendKeyPress(kOmniboxElementId, ui::VKEY_TAB),
WaitForPopupReady());
}
auto WaitForElementToHide(const ui::ElementIdentifier& contents_id,
const DeepQuery& element) {
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kElementHides);
StateChange element_hides;
element_hides.event = kElementHides;
element_hides.where = element;
element_hides.test_function =
"(el) => { let rect = el.getBoundingClientRect(); return rect.width "
"=== 0 && rect.height === 0; }";
return WaitForStateChange(contents_id, element_hides);
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Ensures dropdown resurfaces if it goes away during an Omnibox session.
IN_PROC_BROWSER_TEST_F(OmniboxWebUiInteractiveTest, PopupResurfaces) {
RunTestSequence(
// Enter Gemini mode in Omnibox.
AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUINewTabURL)),
EnterGeminiMode(),
// With a query entered, no matches should show.
EnterText(kOmniboxElementId, u"q"),
InAnyContext(WaitForElementToHide(kPopupWebView, kDropdownContent)),
// Pressing backspace should surface matches.
SendKeyPress(kOmniboxElementId, ui::VKEY_BACK),
InAnyContext(WaitForElementToRender(kPopupWebView, kMatchText)));
}
// Ensures matches show in Gemini mode when there is input, and that
// pressing enter still navigates to Gemini.
IN_PROC_BROWSER_TEST_F(OmniboxWebUiInteractiveTest, GeminiHidesVerbatimMatch) {
RunTestSequence(
// Enter Gemini mode in Omnibox.
AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUINewTabURL)),
EnterGeminiMode(),
// With a query entered, no suggestion match should be shown.
EnterText(kOmniboxElementId, u"query"),
InAnyContext(WaitForElementToHide(kPopupWebView, kDropdownContent)),
// Confirming should navigate to the Gemini URL.
Confirm(kOmniboxElementId),
WaitForWebContentsNavigation(
kNewTab, GURL(OmniboxFieldTrial::kGeminiUrlOverride.Get())));
}
// Ensures Gemini mode's null match; e.g. "<Type search term>" is hidden, and
// that clicking the default search suggestion navigates correctly.
IN_PROC_BROWSER_TEST_F(OmniboxWebUiInteractiveTest, GeminiHidesNullMatch) {
RunTestSequence(
// Enter Gemini mode in Omnibox.
AddInstrumentedTab(kNewTab, GURL(chrome::kChromeUINewTabURL)),
EnterGeminiMode(),
// Ensure the initial match is the default search suggestion.
WaitForElementToRender(kPopupWebView, kMatchText),
InSameContext(CheckJsResultAt(kPopupWebView, kMatchText,
"(el) => el.textContent.replace(/\\s+/g, ' "
"').trim() === '@gemini - Google Search'")),
// Clicking the top match should navigate to a Google search results page.
InSameContext(ClickElement(kPopupWebView, kMatch)),
WaitForWebContentsNavigation(kNewTab),
CheckResult(
[this]() {
return browser()
->tab_strip_model()
->GetActiveWebContents()
->GetLastCommittedURL()
.spec();
},
testing::StartsWith(
"https://www.google.com/search?q=%40gemini&oq=%40gemini")));
}