blob: 600e10acdab813d3d0fb6b5990b74d8bb147b116 [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.
// This class runs CUJ tests for lens overlay. These tests simulate input events
// and cannot be run in parallel.
#include <utility>
#include "base/strings/string_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/themes/theme_service.h"
#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/lens/lens_overlay_controller.h"
#include "chrome/browser/ui/lens/lens_overlay_gen204_controller.h"
#include "chrome/browser/ui/lens/test_lens_overlay_query_controller.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "chrome/test/user_education/interactive_feature_promo_test.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/lens/lens_features.h"
#include "components/lens/lens_overlay_invocation_source.h"
#include "components/lens/lens_overlay_permission_utils.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url_service.h"
#include "components/user_education/views/help_bubble_view.h"
#include "content/public/test/browser_test.h"
#include "media/base/media_switches.h"
#include "ui/base/clipboard/clipboard.h"
namespace {
// The fake server session id.
constexpr char kTestServerSessionId[] = "server_session_id";
// The fake search session id.
constexpr char kTestSearchSessionId[] = "search_session_id";
// The fake suggest signals.
constexpr char kTestSuggestSignals[] = "encoded_image_signals";
constexpr char kDocumentWithNamedElement[] = "/select.html";
constexpr char kDocumentWithImage[] = "/test_visual.html";
constexpr char kDocumentWithVideo[] = "/media/bigbuck-player.html";
lens::Text CreateTestText(const std::vector<std::string>& words) {
lens::Text text;
text.set_content_language("es");
// Create a paragraph.
lens::TextLayout::Paragraph* paragraph =
text.mutable_text_layout()->add_paragraphs();
// Create a line.
lens::TextLayout::Line* line = paragraph->add_lines();
for (size_t i = 0; i < words.size(); ++i) {
lens::TextLayout::Word* word = line->add_words();
word->set_plain_text(words[i]);
word->set_text_separator(" ");
word->mutable_geometry()->mutable_bounding_box()->set_center_x(0.1 * i);
word->mutable_geometry()->mutable_bounding_box()->set_center_y(0.1);
word->mutable_geometry()->mutable_bounding_box()->set_width(0.1);
word->mutable_geometry()->mutable_bounding_box()->set_height(0.1);
word->mutable_geometry()->mutable_bounding_box()->set_coordinate_type(
lens::NORMALIZED);
}
return text;
}
// Stubs out network requests.
class LensOverlayControllerFake : public LensOverlayController {
public:
LensOverlayControllerFake(tabs::TabInterface* tab,
variations::VariationsClient* variations_client,
signin::IdentityManager* identity_manager,
PrefService* pref_service,
syncer::SyncService* sync_service,
ThemeService* theme_service,
Profile* profile)
: LensOverlayController(tab,
variations_client,
identity_manager,
pref_service,
sync_service,
theme_service) {}
std::unique_ptr<lens::LensOverlayQueryController> CreateLensQueryController(
lens::LensOverlayFullImageResponseCallback full_image_callback,
lens::LensOverlayUrlResponseCallback url_callback,
lens::LensOverlaySuggestInputsCallback suggest_inputs_callback,
lens::LensOverlayThumbnailCreatedCallback thumbnail_created_callback,
variations::VariationsClient* variations_client,
signin::IdentityManager* identity_manager,
Profile* profile,
lens::LensOverlayInvocationSource invocation_source,
bool use_dark_mode,
lens::LensOverlayGen204Controller* gen204_controller) override {
auto fake_query_controller =
std::make_unique<lens::TestLensOverlayQueryController>(
full_image_callback, url_callback, suggest_inputs_callback,
thumbnail_created_callback, variations_client, identity_manager,
profile, invocation_source, use_dark_mode, gen204_controller);
// Set up the fake responses for the query controller.
lens::LensOverlayServerClusterInfoResponse cluster_info_response;
cluster_info_response.set_server_session_id(kTestServerSessionId);
cluster_info_response.set_search_session_id(kTestSearchSessionId);
fake_query_controller->set_fake_cluster_info_response(
cluster_info_response);
lens::LensOverlayObjectsResponse objects_response;
objects_response.mutable_text()->CopyFrom(
CreateTestText({"This", "is", "test", "text."}));
objects_response.mutable_cluster_info()->set_server_session_id(
kTestServerSessionId);
objects_response.mutable_cluster_info()->set_search_session_id(
kTestSearchSessionId);
fake_query_controller->set_fake_objects_response(objects_response);
lens::LensOverlayInteractionResponse interaction_response;
interaction_response.set_encoded_response(kTestSuggestSignals);
fake_query_controller->set_fake_interaction_response(interaction_response);
return fake_query_controller;
}
};
class TabFeaturesFake : public tabs::TabFeatures {
public:
TabFeaturesFake() = default;
protected:
std::unique_ptr<LensOverlayController> CreateLensController(
tabs::TabInterface* tab,
Profile* profile) override {
auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
// Set browser color scheme to light mode for consistency.
theme_service->SetBrowserColorScheme(
ThemeService::BrowserColorScheme::kLight);
return std::make_unique<LensOverlayControllerFake>(
tab, profile->GetVariationsClient(),
IdentityManagerFactory::GetForProfile(profile), profile->GetPrefs(),
SyncServiceFactory::GetForProfile(profile), theme_service, profile);
}
};
class LensOverlayControllerCUJTest : public InteractiveFeaturePromoTest {
public:
template <typename... Args>
explicit LensOverlayControllerCUJTest(Args&&... args)
: InteractiveFeaturePromoTest(
UseDefaultTrackerAllowingPromos({std::forward<Args>(args)...})) {
tabs::TabFeatures::ReplaceTabFeaturesForTesting(
base::BindRepeating(&LensOverlayControllerCUJTest::CreateTabFeatures,
base::Unretained(this)));
}
~LensOverlayControllerCUJTest() override = default;
void SetUp() override {
feature_list_.InitWithFeatures(
{lens::features::kLensOverlay,
lens::features::kLensOverlayTranslateButton,
media::kContextMenuSearchForVideoFrame},
{lens::features::kLensOverlayContextualSearchbox});
ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
InteractiveFeaturePromoTest::SetUp();
}
void WaitForTemplateURLServiceToLoad() {
auto* const template_url_service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
search_test_utils::WaitForTemplateURLServiceToLoad(template_url_service);
}
void SetUpOnMainThread() override {
InteractiveFeaturePromoTest::SetUpOnMainThread();
embedded_test_server()->StartAcceptingConnections();
// Permits sharing the page screenshot by default.
PrefService* prefs = browser()->profile()->GetPrefs();
prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, true);
}
void TearDownOnMainThread() override {
EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
InteractiveFeaturePromoTest::TearDownOnMainThread();
// Disallow sharing the page screenshot by default.
PrefService* prefs = browser()->profile()->GetPrefs();
prefs->SetBoolean(lens::prefs::kLensSharingPageScreenshotEnabled, false);
}
std::unique_ptr<tabs::TabFeatures> CreateTabFeatures() {
return std::make_unique<TabFeaturesFake>();
}
InteractiveTestApi::MultiStep OpenLensOverlay() {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement);
// In kDocumentWithNamedElement.
const DeepQuery kPathToBody{
"body",
};
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
kFirstPaintState);
return Steps(
InstrumentTab(kActiveTab), NavigateWebContents(kActiveTab, url),
EnsurePresent(kActiveTab, kPathToBody),
// TODO(https://crbug.com/331859922): This functionality should be built
// into test framework.
PollState(kFirstPaintState,
[this]() {
return browser()
->tab_strip_model()
->GetActiveTab()
->GetContents()
->CompletedFirstVisuallyNonEmptyPaint();
}),
WaitForState(kFirstPaintState, true),
MoveMouseTo(kActiveTab, kPathToBody), ClickMouse(ui_controls::RIGHT),
WaitForShow(RenderViewContextMenu::kRegionSearchItem),
// Required to fully render the menu before selection.
SelectMenuItem(RenderViewContextMenu::kRegionSearchItem,
InputType::kMouse));
}
InteractiveTestApi::MultiStep OpenLensOverlayFromImage() {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
const GURL url = embedded_test_server()->GetURL(kDocumentWithImage);
// In kDocumentWithImage.
const DeepQuery kPathToImg{
"img",
};
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
kFirstPaintState);
return Steps(
InstrumentTab(kActiveTab), NavigateWebContents(kActiveTab, url),
EnsurePresent(kActiveTab, kPathToImg),
// TODO(https://crbug.com/331859922): This functionality should be built
// into test framework.
PollState(kFirstPaintState,
[this]() {
return browser()
->tab_strip_model()
->GetActiveTab()
->GetContents()
->CompletedFirstVisuallyNonEmptyPaint();
}),
WaitForState(kFirstPaintState, true),
MoveMouseTo(kActiveTab, kPathToImg), ClickMouse(ui_controls::RIGHT),
WaitForShow(RenderViewContextMenu::kSearchForImageItem),
// Required to fully render the menu before selection.
SelectMenuItem(RenderViewContextMenu::kSearchForImageItem,
InputType::kMouse));
}
InteractiveTestApi::MultiStep OpenLensOverlayFromVideo() {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kVideoIsPlaying);
const GURL url = embedded_test_server()->GetURL(kDocumentWithVideo);
const char kPlayVideo[] = "(el) => { el.play(); }";
const DeepQuery kPathToVideo{"video"};
constexpr char kMediaIsPlaying[] =
"(el) => { return el.currentTime > 0.1 && !el.paused && !el.ended && "
"el.readyState > 2; }";
StateChange video_is_playing;
video_is_playing.event = kVideoIsPlaying;
video_is_playing.where = kPathToVideo;
video_is_playing.test_function = kMediaIsPlaying;
return Steps(
InstrumentTab(kActiveTab), NavigateWebContents(kActiveTab, url),
EnsurePresent(kActiveTab, kPathToVideo),
ExecuteJsAt(kActiveTab, kPathToVideo, kPlayVideo),
WaitForStateChange(kActiveTab, video_is_playing),
MoveMouseTo(kActiveTab, kPathToVideo), ClickMouse(ui_controls::RIGHT),
WaitForShow(RenderViewContextMenu::kSearchForVideoFrameItem),
// Required to fully render the menu before selection.
SelectMenuItem(RenderViewContextMenu::kSearchForVideoFrameItem,
InputType::kMouse));
}
InteractiveTestApi::MultiStep WaitForScreenshotRendered(
ui::ElementIdentifier overlayId) {
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kScreenshotIsRendered);
const DeepQuery kPathToSelectionOverlay{"lens-overlay-app",
"lens-selection-overlay"};
constexpr char kSelectionOverlayHasBounds[] =
"(el) => { return el.getBoundingClientRect().width > 0 && "
"el.getBoundingClientRect().height > 0; }";
StateChange screenshot_is_rendered;
screenshot_is_rendered.event = kScreenshotIsRendered;
screenshot_is_rendered.where = kPathToSelectionOverlay;
screenshot_is_rendered.test_function = kSelectionOverlayHasBounds;
return Steps(EnsurePresent(overlayId),
WaitForStateChange(overlayId, screenshot_is_rendered));
}
private:
base::test::ScopedFeatureList feature_list_;
};
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User opens lens overlay.
// (3) User clicks the "close" button to close lens overlay.
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
// TODO(b/340343342): Reenable on MSAN.
#if defined(MEMORY_SANITIZER) || BUILDFLAG(IS_MAC)
#define MAYBE_OpenAndClose DISABLED_OpenAndClose
#else
#define MAYBE_OpenAndClose OpenAndClose
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, MAYBE_OpenAndClose) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement);
// In kDocumentWithNamedElement.
const DeepQuery kPathToBody{
"body",
};
// In the lens overlay.
const DeepQuery kPathToCloseButton{
"lens-overlay-app",
"#closeButton",
};
constexpr char kClickFn[] = "(el) => { el.click(); }";
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget associated
// with a tab rather than a browser window, so by convention gets its own
// element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy.
InSameContext(Steps(EnsurePresent(kOverlayId, kPathToCloseButton),
ExecuteJsAt(kOverlayId, kPathToCloseButton, kClickFn,
ExecuteJsMode::kFireAndForget),
WaitForHide(kOverlayId))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User opens lens overlay.
// (3) User presses the escape key to close lens overlay.
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
// TODO(b/340343342): Reenable on windows.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || defined(MEMORY_SANITIZER)
#define MAYBE_EscapeKeyClose DISABLED_EscapeKeyClose
#else
#define MAYBE_EscapeKeyClose EscapeKeyClose
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, MAYBE_EscapeKeyClose) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement);
// In kDocumentWithNamedElement.
const DeepQuery kPathToBody{
"body",
};
const ui::Accelerator escape_key(ui::VKEY_ESCAPE, ui::EF_NONE);
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget associated
// with a tab rather than a browser window, so by convention gets its own
// element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy.
InSameContext(Steps(SendAccelerator(kOverlayId, escape_key),
WaitForHide(kOverlayId))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User opens lens overlay.
// (3) User highlights some text.
// (4) User presses CTRL+C on some text.
// (5) Highlighted text gets copied.
// Disabled: apparent hang (crbug.com/347282479)
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest,
DISABLED_CopyKeyCommandCopies) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(ui::test::PollingStateObserver<bool>,
kTextCopiedState);
const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement);
// In kDocumentWithNamedElement.
const DeepQuery kPathToBody{
"body",
};
// Path to text
const DeepQuery kPathToWord{
"lens-overlay-app",
"lens-selection-overlay",
"lens-text-layer",
".word",
};
const ui::Accelerator ctrl_c_accelerator(ui::VKEY_C, ui::EF_CONTROL_DOWN);
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget associated
// with a tab rather than a browser window, so by convention gets its own
// element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy. Then
// click a word to highlight it. Flush tasks after click to prevent
// flakiness.
InSameContext(Steps(WaitForShow(LensOverlayController::kOverlayId),
WaitForScreenshotRendered(kOverlayId),
EnsurePresent(kOverlayId, kPathToWord),
MoveMouseTo(kOverlayId, kPathToWord),
ClickMouse(ui_controls::LEFT))),
// Clicking the text should have opened the side panel with the results
// frame.
InAnyContext(Steps(InstrumentNonTabWebView(
kOverlaySidePanelWebViewId,
LensOverlayController::kOverlaySidePanelWebViewId))),
// Press CTRL+C command and ensure the highlighted text is saved to
// clipboard. We send the command to the side panel web view because in
// actual usage, the side panel is the view with focus so it receives
// the event right after selecting text.
InAnyContext(
Steps(SendAccelerator(kOverlaySidePanelWebViewId, ctrl_c_accelerator),
PollState(kTextCopiedState,
[&]() {
ui::Clipboard* clipboard =
ui::Clipboard::GetForCurrentThread();
std::u16string clipboard_text;
clipboard->ReadText(ui::ClipboardBuffer::kCopyPaste,
/* data_dst = */ nullptr,
&clipboard_text);
return base::EqualsASCII(clipboard_text, "This");
}),
WaitForState(kTextCopiedState, true))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User opens lens overlay.
// (3) User makes a selection that opens the results side panel.
// (4) User presses the escape key to close lens overlay.
// TODO(crbug.com/340343342): Reenable on Windows, Mac, Linux, and ChromeOS
// (dbg) Tests.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || defined(MEMORY_SANITIZER) || \
BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH)
#define MAYBE_EscapeKeyCloseWithResultsPanel \
DISABLED_EscapeKeyCloseWithResultsPanel
#else
#define MAYBE_EscapeKeyCloseWithResultsPanel EscapeKeyCloseWithResultsPanel
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest,
MAYBE_EscapeKeyCloseWithResultsPanel) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement);
auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser());
// In kDocumentWithNamedElement.
const DeepQuery kPathToBody{
"body",
};
const DeepQuery kPathToRegionSelection{
"lens-overlay-app",
"lens-selection-overlay",
"#regionSelectionLayer",
};
const DeepQuery kPathToResultsFrame{
"lens-side-panel-app",
"#results",
};
auto off_center_point = base::BindLambdaForTesting([browser_view]() {
gfx::Point off_center =
browser_view->contents_web_view()->bounds().CenterPoint();
off_center.Offset(100, 100);
return off_center;
});
const ui::Accelerator escape_key(ui::VKEY_ESCAPE, ui::EF_NONE);
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy. Then do
// a drag offset from the center. Flush tasks after drag to prevent
// flakiness.
InSameContext(Steps(WaitForShow(LensOverlayController::kOverlayId),
WaitForScreenshotRendered(kOverlayId),
EnsurePresent(kOverlayId, kPathToRegionSelection),
MoveMouseTo(LensOverlayController::kOverlayId),
DragMouseTo(off_center_point))),
// The drag should have opened the side panel with the results frame.
InAnyContext(Steps(
InstrumentNonTabWebView(
kOverlaySidePanelWebViewId,
LensOverlayController::kOverlaySidePanelWebViewId),
EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame))),
// Press the escape key to and ensure the overlay closes.
InSameContext(
Steps(SendAccelerator(kOverlaySidePanelWebViewId, escape_key),
WaitForHide(kOverlayId))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User opens lens overlay.
// (3) User drags to select a manual region on the overlay.
// (4) Side panel opens with results.
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SelectManualRegion DISABLED_SelectManualRegion
#else
#define MAYBE_SelectManualRegion SelectManualRegion
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, MAYBE_SelectManualRegion) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser());
const DeepQuery kPathToRegionSelection{
"lens-overlay-app",
"lens-selection-overlay",
"#regionSelectionLayer",
};
const DeepQuery kPathToResultsFrame{
"lens-side-panel-app",
"#results",
};
auto off_center_point = base::BindLambdaForTesting([browser_view]() {
gfx::Point off_center =
browser_view->contents_web_view()->bounds().CenterPoint();
off_center.Offset(100, 100);
return off_center;
});
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy. Then do
// a drag offset from the center. Flush tasks after drag to prevent
// flakiness.
InSameContext(Steps(WaitForShow(LensOverlayController::kOverlayId),
WaitForScreenshotRendered(kOverlayId),
EnsurePresent(kOverlayId, kPathToRegionSelection),
MoveMouseTo(LensOverlayController::kOverlayId),
DragMouseTo(off_center_point))),
// The drag should have opened the side panel with the results frame.
InAnyContext(Steps(
InstrumentNonTabWebView(
kOverlaySidePanelWebViewId,
LensOverlayController::kOverlaySidePanelWebViewId),
EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User right-clicks an image and opens lens overlay.
// (3) Side panel opens with results.
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SearchForImage DISABLED_SearchForImage
#else
#define MAYBE_SearchForImage SearchForImage
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, MAYBE_SearchForImage) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
const DeepQuery kPathToRegionSelection{
"lens-overlay-app",
"lens-selection-overlay",
"#regionSelectionLayer",
};
const DeepQuery kPathToResultsFrame{
"lens-side-panel-app",
"#results",
};
RunTestSequence(
OpenLensOverlayFromImage(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// The side panel should open with the results frame.
InAnyContext(Steps(
InstrumentNonTabWebView(
kOverlaySidePanelWebViewId,
LensOverlayController::kOverlaySidePanelWebViewId),
EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame))));
}
// This tests the following CUJ:
// (1) User navigates to a website.
// (2) User right-clicks a video and opens "Search with Google Lens".
// (3) Side panel opens with results.
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
#if BUILDFLAG(IS_MAC)
#define MAYBE_SearchForVideoFrame DISABLED_SearchForVideoFrame
#else
#define MAYBE_SearchForVideoFrame SearchForVideoFrame
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest,
MAYBE_SearchForVideoFrame) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId);
const DeepQuery kPathToRegionSelection{
"lens-overlay-app",
"lens-selection-overlay",
"#regionSelectionLayer",
};
const DeepQuery kPathToResultsFrame{
"lens-side-panel-app",
"#results",
};
RunTestSequence(
OpenLensOverlayFromVideo(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// The side panel should open with the results frame.
InAnyContext(Steps(
InstrumentNonTabWebView(
kOverlaySidePanelWebViewId,
LensOverlayController::kOverlaySidePanelWebViewId),
EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame))));
}
class LensOverlayControllerPromoTest : public LensOverlayControllerCUJTest {
public:
LensOverlayControllerPromoTest()
: LensOverlayControllerCUJTest(
feature_engagement::kIPHSidePanelLensOverlayPinnableFeature,
feature_engagement::
kIPHSidePanelLensOverlayPinnableFollowupFeature) {}
~LensOverlayControllerPromoTest() override = default;
};
// TODO(b/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ShowsPromo DISABLED_ShowsPromo
#else
#define MAYBE_ShowsPromo ShowsPromo
#endif
IN_PROC_BROWSER_TEST_F(LensOverlayControllerPromoTest, MAYBE_ShowsPromo) {
// Use the same setup and initial sequence as `SelectManualRegion` above in
// order to trigger the side panel.
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser());
const DeepQuery kPathToRegionSelection{
"lens-overlay-app",
"lens-selection-overlay",
"#regionSelectionLayer",
};
const DeepQuery kPathToResultsFrame{
"lens-side-panel-app",
"#results",
};
auto off_center_point = base::BindLambdaForTesting([browser_view]() {
gfx::Point off_center =
browser_view->contents_web_view()->bounds().CenterPoint();
off_center.Offset(100, 100);
return off_center;
});
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy. Then do
// a drag offset from the center. Flush tasks after drag to prevent
// flakiness.
InSameContext(Steps(WaitForShow(LensOverlayController::kOverlayId),
WaitForScreenshotRendered(kOverlayId),
EnsurePresent(kOverlayId, kPathToRegionSelection),
MoveMouseTo(LensOverlayController::kOverlayId),
DragMouseTo(off_center_point))),
// The drag should have opened the side panel with the results frame.
WaitForShow(LensOverlayController::kOverlaySidePanelWebViewId),
// Wait for the initial "do you want to pin this?" help bubble.
WaitForPromo(feature_engagement::kIPHSidePanelLensOverlayPinnableFeature),
// Pin the side panel. This should dismiss the IPH, but also launch
// another, so wait for the first hide to avoid the next check picking up
// the wrong help bubble.
PressButton(kSidePanelPinButtonElementId),
// Specifying transition-only-on-event here means even if there is a
// different help bubble already in-frame, this step will succeed when any
// help bubble goes away.
WaitForHide(
user_education::HelpBubbleView::kHelpBubbleElementIdForTesting)
.SetTransitionOnlyOnEvent(true),
// A second IPH should appear showing where the item was pinned.
WaitForPromo(
feature_engagement::kIPHSidePanelLensOverlayPinnableFollowupFeature));
}
class LensOverlayControllerTranslatePromoTest
: public LensOverlayControllerCUJTest {
public:
LensOverlayControllerTranslatePromoTest()
: LensOverlayControllerCUJTest(
feature_engagement::kIPHLensOverlayTranslateButtonFeature) {}
~LensOverlayControllerTranslatePromoTest() override = default;
};
// TODO(crbug.com/355224013): Disabled on mac because the mac interaction test
// util implementation does not support setting the input (mouse / keyboard)
// type for a context menu item selection.
#if BUILDFLAG(IS_MAC)
#define MAYBE_ShowsTranslatePromo DISABLED_ShowsTranslatePromo
#else
#define MAYBE_ShowsTranslatePromo ShowsTranslatePromo
#endif
// This tests the following promo flow:
// (1) User opens the Lens Overlay.
// (2) Promo shows. After, user clicks the translate button.
// (3) Promo hides.
IN_PROC_BROWSER_TEST_F(LensOverlayControllerTranslatePromoTest,
MAYBE_ShowsTranslatePromo) {
WaitForTemplateURLServiceToLoad();
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId);
const DeepQuery kPathToTranslateButton{
"lens-overlay-app",
"#translateButton",
"#translateEnableButton",
};
RunTestSequence(
OpenLensOverlay(),
// The overlay controller is an independent floating widget
// associated with a tab rather than a browser window, so by
// convention gets its own element context.
InAnyContext(Steps(
InstrumentNonTabWebView(kOverlayId,
LensOverlayController::kOverlayId),
WaitForWebContentsReady(
kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)))),
// Wait for the webview to finish loading to prevent re-entrancy.
InSameContext(Steps(WaitForShow(LensOverlayController::kOverlayId),
WaitForScreenshotRendered(kOverlayId),
EnsurePresent(kOverlayId, kPathToTranslateButton))),
// Wait for the initial translate promo help bubble.
WaitForPromo(feature_engagement::kIPHLensOverlayTranslateButtonFeature),
// Click the translate button element.
ClickElement(kOverlayId, kPathToTranslateButton),
WaitForHide(
user_education::HelpBubbleView::kHelpBubbleElementIdForTesting));
}
} // namespace