| // 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/functional/bind.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/run_until.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/lens_search_controller.h" |
| #include "chrome/browser/ui/lens/test_lens_search_controller.h" |
| #include "chrome/browser/ui/tabs/public/tab_features.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/toolbar/app_menu_model.h" |
| #include "chrome/browser/ui/views/location_bar/lens_overlay_homework_page_action_icon_view.h" |
| #include "chrome/common/webui_url_constants.h" |
| #include "chrome/test/base/search_test_utils.h" |
| #include "chrome/test/base/ui_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/pdf/browser/pdf_document_helper.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/browser/render_view_host.h" |
| #include "content/public/test/browser_test.h" |
| #include "media/base/media_switches.h" |
| #include "net/base/mock_network_change_notifier.h" |
| #include "net/base/network_change_notifier.h" |
| #include "ui/base/clipboard/clipboard.h" |
| #include "ui/base/clipboard/clipboard_format_type.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| #include "ui/gfx/geometry/point.h" |
| |
| namespace { |
| |
| constexpr char kDocumentWithNamedElement[] = "/select.html"; |
| constexpr char kDocumentWithImage[] = "/test_visual.html"; |
| constexpr char kDocumentWithVideo[] = "/media/bigbuck-player.html"; |
| constexpr char kPdfDocument[] = "/pdf/test.pdf"; |
| |
| class LensOverlayControllerCUJTest : public InteractiveFeaturePromoTest { |
| public: |
| template <typename... Args> |
| explicit LensOverlayControllerCUJTest(Args&&... args) |
| : InteractiveFeaturePromoTest( |
| UseDefaultTrackerAllowingPromos({std::forward<Args>(args)...})) { |
| lens_search_controller_override_ = |
| tabs::TabFeatures::GetUserDataFactoryForTesting().AddOverrideForTesting( |
| base::BindRepeating([](tabs::TabInterface& tab) { |
| return std::make_unique<lens::TestLensSearchController>(&tab); |
| })); |
| } |
| ~LensOverlayControllerCUJTest() override = default; |
| |
| void SetUp() override { |
| SetUpFeatureList(); |
| ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); |
| InteractiveFeaturePromoTest::SetUp(); |
| } |
| |
| virtual void SetUpFeatureList() { |
| feature_list_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/{{lens::features::kLensOverlay, {}}, |
| {lens::features::kLensOverlayTranslateButton, {}}, |
| {media::kContextMenuSearchForVideoFrame, {}}, |
| {lens::features::kLensOverlayContextualSearchbox, |
| {{"use-pdfs-as-context", "true"}, |
| {"auto-focus-searchbox", "false"}}}}, |
| /*disabled_features=*/{lens::features::kLensSearchZeroStateCsb}); |
| } |
| |
| 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); |
| prefs->SetBoolean(lens::prefs::kLensSharingPageContentEnabled, 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); |
| } |
| |
| InteractiveTestApi::MultiStep OpenArbitraryNewTab() { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTab); |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| |
| // In kDocumentWithNamedElement. |
| const DeepQuery kPathToBody{ |
| "body", |
| }; |
| |
| return Steps(AddInstrumentedTab(kNewTab, url), |
| EnsurePresent(kNewTab, kPathToBody), |
| WaitForWebContentsReady(kNewTab)); |
| } |
| |
| InteractiveTestApi::MultiStep OpenLensOverlay() { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| |
| // In kDocumentWithNamedElement. |
| const DeepQuery kPathToBody{ |
| "body", |
| }; |
| |
| return Steps(InstrumentTab(kActiveTab), |
| NavigateWebContents(kActiveTab, url), |
| EnsurePresent(kActiveTab, kPathToBody), |
| WaitForWebContentsPainted(kActiveTab), |
| |
| // Open the three dot menu and select the Lens Overlay option. |
| PressButton(kToolbarAppMenuButtonElementId), |
| WaitForShow(AppMenuModel::kShowLensOverlay), |
| SelectMenuItem(AppMenuModel::kShowLensOverlay)); |
| } |
| |
| InteractiveTestApi::MultiStep OpenLensOverlayFromImage() { |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithImage); |
| |
| // In kDocumentWithImage. |
| const DeepQuery kPathToImg{ |
| "img", |
| }; |
| |
| return Steps(InstrumentTab(kActiveTab), |
| NavigateWebContents(kActiveTab, url), |
| WaitForWebContentsPainted(kActiveTab), |
| |
| MoveMouseTo(kActiveTab, kPathToImg), |
| MayInvolveNativeContextMenu( |
| ClickMouse(ui_controls::RIGHT), |
| 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), |
| MayInvolveNativeContextMenu( |
| ClickMouse(ui_controls::RIGHT), |
| 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)); |
| } |
| |
| protected: |
| base::test::ScopedFeatureList feature_list_; |
| |
| private: |
| ui::UserDataFactory::ScopedOverride lens_search_controller_override_; |
| }; |
| |
| // 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. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| // Wait for the webview to finish loading to prevent re-entrancy. |
| InSameContext(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. |
| #if BUILDFLAG(IS_LINUX) && defined(ADDRESS_SANITIZER) |
| // Flaky on ASAN on Linux. |
| #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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL)), |
| WaitForWebContentsPainted(kOverlayId)), |
| // Wait for the webview to finish loading to prevent re-entrancy. |
| InSameContext(FocusWebContents(kOverlayId), |
| SendAccelerator(kOverlayId, escape_key), |
| WaitForHide(kOverlayId))); |
| } |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website. |
| // (2) User opens lens overlay. |
| // (3) User selects a region with text. |
| // (4) User presses CTRL+C on some text. |
| // (5) Text in region gets copied. |
| // TODO(crbug.com/399520257): Fix test failure on Linux, and ASAN. |
| #if BUILDFLAG(IS_LINUX) || defined(ADDRESS_SANITIZER) |
| // Flaky on ASAN, and on Linux. |
| #define MAYBE_CopyKeyCommandCopies DISABLED_CopyKeyCommandCopies |
| #else |
| #define MAYBE_CopyKeyCommandCopies CopyKeyCommandCopies |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, |
| MAYBE_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); |
| auto top_left_point = base::BindLambdaForTesting([&](ui::TrackedElement* el) { |
| return gfx::Point(el->AsA<views::TrackedElementViews>() |
| ->view() |
| ->GetBoundsInScreen() |
| .origin()); |
| }); |
| |
| // Path to region selection layer. |
| const DeepQuery kPathToRegionSelection{ |
| "lens-overlay-app", |
| "lens-selection-overlay", |
| "region-selection", |
| }; |
| |
| 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // Wait for the webview to finish loading to prevent re-entrancy. Then |
| // click the center of the region selection layer to select a region. |
| // Flush tasks after click to prevent flakiness. |
| InSameContext( |
| WaitForShow(LensOverlayController::kOverlayId), |
| WaitForScreenshotRendered(kOverlayId), |
| EnsurePresent(kOverlayId, kPathToRegionSelection), |
| MoveMouseTo(kOverlayId, kPathToRegionSelection), |
| DragMouseTo(LensOverlayController::kOverlayId, top_left_point)), |
| |
| // Clicking the overlay should have opened the side panel with the results |
| // frame. |
| InAnyContext(InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId), |
| WaitForWebContentsPainted(kOverlaySidePanelWebViewId)), |
| |
| // Press CTRL+C command and ensure the selected region is saved to |
| // clipboard. 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 the region. |
| InSameContext( |
| WaitForShow(kOverlaySidePanelWebViewId), |
| FocusWebContents(kOverlaySidePanelWebViewId), |
| 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 is test text."); |
| }), |
| 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. |
| #if BUILDFLAG(IS_LINUX) && defined(ADDRESS_SANITIZER) |
| // Flaky on ASAN on Linux. |
| #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); |
| |
| // 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([&](ui::TrackedElement* el) { |
| return el->AsA<views::TrackedElementViews>() |
| ->view() |
| ->GetBoundsInScreen() |
| .CenterPoint() + |
| gfx::Vector2d(50, 50); |
| }); |
| |
| 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( |
| 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( |
| WaitForShow(LensOverlayController::kOverlayId), |
| WaitForScreenshotRendered(kOverlayId), |
| EnsurePresent(kOverlayId, kPathToRegionSelection), |
| MoveMouseTo(LensOverlayController::kOverlayId), |
| DragMouseTo(LensOverlayController::kOverlayId, off_center_point)), |
| |
| // The drag should have opened the side panel with the results frame. |
| InAnyContext( |
| InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId), |
| WaitForWebContentsPainted(kOverlaySidePanelWebViewId), |
| EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame)), |
| // Press the escape key to and ensure the overlay closes. |
| InSameContext(WaitForShow(kOverlaySidePanelWebViewId), |
| FocusWebContents(kOverlaySidePanelWebViewId), |
| 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(crbug.com/355224013): Re-enable this test |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, |
| DISABLED_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( |
| 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(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( |
| 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. |
| // 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // The side panel should open with the results frame. |
| InAnyContext( |
| 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. |
| // 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // The side panel should open with the results frame. |
| InAnyContext( |
| InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| EnsurePresent(kOverlaySidePanelWebViewId, kPathToResultsFrame))); |
| } |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a PDF. |
| // (2) User opens lens overlay. |
| // (3) The CSB should say "Ask about this document" |
| // (3) User make a query |
| // (4) This side panel opens |
| // (5) The CSB should say "Ask about this document" |
| // (6) The user navigates to a webpage. |
| // (7) The CSB should say "Ask about this page" |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerCUJTest, NavigationsUpdateCSB) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); |
| DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kHintTextUpdatedEvent); |
| |
| const GURL pdf_url = embedded_test_server()->GetURL(kPdfDocument); |
| |
| // Paths to searchbox hint text. |
| const DeepQuery kPathToOverlaySearchboxInput{ |
| "lens-overlay-app", |
| "cr-searchbox", |
| "input", |
| }; |
| const DeepQuery kPathToSidePanelSearchboxInput{ |
| "lens-side-panel-app", |
| "cr-searchbox", |
| "input", |
| }; |
| const DeepQuery kPathToOverlayGhostLoaderText{ |
| "lens-overlay-app", "cr-searchbox-ghost-loader", "#hint-text1"}; |
| const DeepQuery kPathToSidePanelGhostLoaderText{ |
| "lens-side-panel-app", "cr-searchbox-ghost-loader", "#hint-text1"}; |
| |
| // Helper function to check for specific text in an element. |
| auto CheckSearchboxHintText = [](ui::ElementIdentifier web_contents_id, |
| const DeepQuery& query, |
| const std::string& expected_text) { |
| return CheckJsResultAt(web_contents_id, query, |
| base::StringPrintf("el => el.placeholder === '%s'", |
| expected_text.c_str())); |
| }; |
| auto CheckGhostLoaderText = [](ui::ElementIdentifier web_contents_id, |
| const DeepQuery& query, |
| const std::string& expected_text) { |
| return CheckJsResultAt( |
| web_contents_id, query, |
| base::StringPrintf("el => el.innerText.trim() === '%s'", |
| expected_text.c_str())); |
| }; |
| |
| // State change to wait for the hint text to update. |
| StateChange hint_text_updated; |
| hint_text_updated.event = kHintTextUpdatedEvent; |
| hint_text_updated.type = StateChange::Type::kExistsAndConditionTrue; |
| hint_text_updated.where = kPathToSidePanelSearchboxInput; |
| hint_text_updated.test_function = |
| "el => el.placeholder === 'Ask about this document'"; |
| |
| RunTestSequence( |
| // TODO(crbug.com/355224013): Ideally, this opens with a PDF to start, |
| // but the right click menu item is not accessible in the UI test on PDF. |
| // Once these tests can open without the rigth click menu, this should be |
| // updated to open a PDF. |
| 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // The CSB should be in the overlay with the text "Ask about this |
| // document". |
| InSameContext( |
| WaitForShow(LensOverlayController::kOverlayId), |
| WaitForScreenshotRendered(kOverlayId), |
| CheckSearchboxHintText(kOverlayId, kPathToOverlaySearchboxInput, |
| "Ask about this page"), |
| CheckGhostLoaderText(kOverlayId, kPathToOverlayGhostLoaderText, |
| "Generating suggestions for this page…")), |
| |
| // The use makes a query in the searchbox and the side panel opens. |
| InSameContext( |
| // Focus the overlay to receive input events. |
| FocusWebContents(kOverlayId), |
| |
| // Focus the searchbox. |
| ExecuteJsAt(kOverlayId, kPathToOverlaySearchboxInput, |
| "(el) => { el.focus(); }", |
| ExecuteJsMode::kWaitForCompletion), |
| |
| // Emulate focus into the searchbox. |
| ExecuteJsAt( |
| kOverlayId, kPathToOverlaySearchboxInput, |
| base::StringPrintf( |
| "(el) => { el.value = '%s'; el.dispatchEvent(new " |
| "Event('input', { bubbles: true })); el.dispatchEvent(new " |
| "Event('change', { bubbles: true }));}", |
| "test query"), |
| ExecuteJsMode::kWaitForCompletion), |
| |
| // Simulate the enter key being pressed. |
| ExecuteJsAt( |
| kOverlayId, kPathToOverlaySearchboxInput, |
| base::StringPrintf( |
| "(el) => { el.dispatchEvent(new KeyboardEvent('keydown', { " |
| "key:'%s', bubbles: true }));}", |
| "Enter"), |
| ExecuteJsMode::kFireAndForget)), |
| |
| // Side panel should open. |
| InAnyContext(InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId)), |
| |
| // The CSB in the side panel should say "Ask about this document" |
| InSameContext( |
| CheckSearchboxHintText(kOverlaySidePanelWebViewId, |
| kPathToSidePanelSearchboxInput, |
| "Ask about this page"), |
| CheckGhostLoaderText(kOverlaySidePanelWebViewId, |
| kPathToSidePanelGhostLoaderText, |
| "Generating suggestions for this page…")), |
| |
| // The user navigates to a webpage. |
| InAnyContext(InstrumentTab(kActiveTab), |
| NavigateWebContents(kActiveTab, pdf_url)), |
| |
| // The CSB in the overlay should eventually say "Ask about this page" |
| InAnyContext( |
| WaitForStateChange(kOverlaySidePanelWebViewId, hint_text_updated), |
| CheckSearchboxHintText(kOverlaySidePanelWebViewId, |
| kPathToSidePanelSearchboxInput, |
| "Ask about this document"), |
| CheckGhostLoaderText(kOverlaySidePanelWebViewId, |
| kPathToSidePanelGhostLoaderText, |
| "Generating suggestions for this document…"))); |
| } |
| |
| class LensOverlayControllerPromoTest : public LensOverlayControllerCUJTest { |
| public: |
| LensOverlayControllerPromoTest() |
| : LensOverlayControllerCUJTest( |
| feature_engagement::kIPHSidePanelLensOverlayPinnableFeature, |
| feature_engagement:: |
| kIPHSidePanelLensOverlayPinnableFollowupFeature) {} |
| ~LensOverlayControllerPromoTest() override = default; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerPromoTest, 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( |
| 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(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; |
| }; |
| |
| // This tests the following promo flow: |
| // (1) User opens the Lens Overlay. |
| // (2) Promo shows. After, user clicks the translate button. |
| // (3) Promo hides. |
| // TODO(crbug.com/392907122): Re-enable this test once the translate button is |
| // in a launchable state. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerTranslatePromoTest, |
| DISABLED_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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // Wait for the webview to finish loading to prevent re-entrancy. |
| InSameContext(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)); |
| } |
| |
| class LensPreselectionBubbleInteractiveUiTest |
| : public LensOverlayControllerCUJTest { |
| public: |
| LensPreselectionBubbleInteractiveUiTest() = default; |
| ~LensPreselectionBubbleInteractiveUiTest() override = default; |
| LensPreselectionBubbleInteractiveUiTest( |
| const LensPreselectionBubbleInteractiveUiTest&) = delete; |
| void operator=(const LensPreselectionBubbleInteractiveUiTest&) = delete; |
| |
| auto SetConnectionOffline() { |
| return Do(base::BindLambdaForTesting([&]() { |
| // Set the network connection type to being offline. |
| scoped_mock_network_change_notifier = |
| std::make_unique<net::test::ScopedMockNetworkChangeNotifier>(); |
| scoped_mock_network_change_notifier->mock_network_change_notifier() |
| ->SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE); |
| })); |
| } |
| |
| void TearDownOnMainThread() override { |
| scoped_mock_network_change_notifier.reset(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| raw_ptr<views::Widget> preselection_widget_; |
| std::unique_ptr<net::test::ScopedMockNetworkChangeNotifier> |
| scoped_mock_network_change_notifier; |
| }; |
| |
| // This tests the following CUJ: |
| // (1) User opens the Lens Overlay while offline.. |
| // (2) The user presses the exit button in the preselection bubble. |
| // (3) The overlay should close. |
| IN_PROC_BROWSER_TEST_F(LensPreselectionBubbleInteractiveUiTest, |
| PermissionBubbleOffline) { |
| RunTestSequence(EnsureNotPresent(kLensPreselectionBubbleExitButtonElementId), |
| SetConnectionOffline(), OpenLensOverlay(), |
| WaitForShow(kLensPreselectionBubbleExitButtonElementId), |
| PressButton(kLensPreselectionBubbleExitButtonElementId), |
| WaitForHide(LensOverlayController::kOverlayId)); |
| } |
| |
| using LensOverlayControllerReturnToPageCUJTest = LensOverlayControllerCUJTest; |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website. |
| // (2) User opens lens overlay and the side panel opens. |
| // (3) User navigates to a new page in the same tab. |
| // (4) The overlay should close, but the side panel should remain open. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerReturnToPageCUJTest, |
| HidesOverlayOnClobberTab) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kActiveTab); |
| |
| const GURL second_url = embedded_test_server()->GetURL(kDocumentWithVideo); |
| auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| |
| const DeepQuery kPathToRegionSelection{ |
| "lens-overlay-app", |
| "lens-selection-overlay", |
| "#regionSelectionLayer", |
| }; |
| |
| 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( |
| // Open lens overlay. |
| 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( |
| 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. |
| InSameContext(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), |
| |
| // Navigate to another page in the same tab. |
| // The user navigates to a webpage. |
| InAnyContext(InstrumentTab(kActiveTab), |
| NavigateWebContents(kActiveTab, second_url)), |
| |
| // Ensure overlay is not visible but side panel is. |
| WaitForHide(kOverlayId), EnsureNotPresent(kOverlayId), |
| EnsurePresent(LensOverlayController::kOverlaySidePanelWebViewId)); |
| } |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website. |
| // (2) User opens lens overlay. |
| // (3) User searches a region and the side panel opens. |
| // (4) User clicks the close button. |
| // (5) The overlay should close, but the side panel should remain open. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerReturnToPageCUJTest, |
| CloseButtonHidesOnlyOverlay) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId); |
| |
| auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| |
| // In kDocumentWithNamedElement. |
| const DeepQuery kPathToBody{ |
| "body", |
| }; |
| |
| // In the lens overlay. |
| const DeepQuery kPathToCloseButton{ |
| "lens-overlay-app", |
| "lens-selection-overlay", |
| "#closeButton", |
| }; |
| const DeepQuery kPathToRegionSelection{ |
| "lens-overlay-app", |
| "lens-selection-overlay", |
| "#regionSelectionLayer", |
| }; |
| constexpr char kClickFn[] = "(el) => { el.click(); }"; |
| |
| 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( |
| 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. |
| InSameContext(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 webview to finish loading to prevent re-entrancy. |
| InSameContext(EnsurePresent(kOverlayId, kPathToCloseButton), |
| ExecuteJsAt(kOverlayId, kPathToCloseButton, kClickFn, |
| ExecuteJsMode::kFireAndForget), |
| WaitForHide(kOverlayId)), |
| |
| // Ensure side panel is still visible. |
| EnsurePresent(LensOverlayController::kOverlaySidePanelWebViewId)); |
| } |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website. |
| // (2) User opens lens overlay. |
| // (3) User searches a region and the side panel opens. |
| // (4) User opens a new tab. |
| // (5) The overlay and side panel should close/hide. |
| // (6) User navigates back to the original tab. |
| // (7) The overlay and side panel should reshow. |
| // NOTE: The image context menu item is not supported on Mac. |
| #if BUILDFLAG(IS_MAC) |
| #define MAYBE_OverlayReshowsWhenTabIsSwitchedBackToForeground \ |
| DISABLED_OverlayReshowsWhenTabIsSwitchedBackToForeground |
| #else |
| #define MAYBE_OverlayReshowsWhenTabIsSwitchedBackToForeground \ |
| OverlayReshowsWhenTabIsSwitchedBackToForeground |
| #endif |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerReturnToPageCUJTest, |
| MAYBE_OverlayReshowsWhenTabIsSwitchedBackToForeground) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId); |
| |
| auto* const browser_view = BrowserView::GetBrowserViewForBrowser(browser()); |
| |
| 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( |
| 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // The opening from an image should have opened the side panel with the |
| // results frame. |
| WaitForShow(LensOverlayController::kOverlaySidePanelWebViewId), |
| |
| // Wait for the webview to finish loading to prevent re-entrancy. |
| OpenArbitraryNewTab(), |
| |
| // Ensure side panel and overlay are not visible. |
| EnsureNotPresent(LensOverlayController::kOverlayId), |
| EnsureNotPresent(LensOverlayController::kOverlaySidePanelWebViewId), |
| |
| // Switch back to the original tab. |
| SelectTab(kTabStripElementId, 0), |
| |
| // Overlay and side panel should be visible again. |
| WaitForShow(LensOverlayController::kOverlayId), |
| WaitForShow(LensOverlayController::kOverlaySidePanelWebViewId)); |
| } |
| |
| class LensOverlayControllerStraightToSrpTest |
| : public LensOverlayControllerCUJTest { |
| public: |
| LensOverlayControllerStraightToSrpTest() = default; |
| ~LensOverlayControllerStraightToSrpTest() override = default; |
| LensOverlayControllerStraightToSrpTest( |
| const LensOverlayControllerStraightToSrpTest&) = delete; |
| void operator=(const LensOverlayControllerStraightToSrpTest&) = delete; |
| |
| void SetUpFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {base::test::FeatureRefAndParams( |
| lens::features::kLensOverlayStraightToSrp, {}), |
| base::test::FeatureRefAndParams( |
| lens::features::kLensOverlayEduActionChip, |
| {{"url-allow-filters", "[\"*\"]"}, |
| {"url-path-match-allow-filters", "[\"select\"]"}})}, |
| {}); |
| } |
| }; |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website that triggers the homework action chip. |
| // (2) User clicks the action chip and the side panel opens with CSB results. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerStraightToSrpTest, |
| HomeworkActionChipOpensCsbResults) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId); |
| |
| const DeepQuery kPathToSidePanelSearchboxInput{ |
| "lens-side-panel-app", |
| "cr-searchbox", |
| "input", |
| }; |
| |
| // Helper function to check for specific text in an element. |
| auto CheckSearchboxValue = [](ui::ElementIdentifier web_contents_id, |
| const DeepQuery& query, |
| const std::string& expected_text) { |
| return CheckJsResultAt( |
| web_contents_id, query, |
| base::StringPrintf("el => el.value === '%s'", expected_text.c_str())); |
| }; |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| // Navigate to a matching page. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); |
| // We need to wait for paint in order to take a screenshot of the page. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->CompletedFirstVisuallyNonEmptyPaint(); |
| })); |
| |
| RunTestSequence( |
| PressButton(kLensOverlayHomeworkPageActionIconElementId), |
| // Side panel should open. |
| InAnyContext(InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId)), |
| |
| // The CSB query in the side panel should say "help me with this" |
| InSameContext(CheckSearchboxValue(kOverlaySidePanelWebViewId, |
| kPathToSidePanelSearchboxInput, |
| "help me with this"))); |
| } |
| |
| class LensOverlayControllerStraightToSrpCustomQueryTest |
| : public LensOverlayControllerCUJTest { |
| public: |
| LensOverlayControllerStraightToSrpCustomQueryTest() = default; |
| ~LensOverlayControllerStraightToSrpCustomQueryTest() override = default; |
| LensOverlayControllerStraightToSrpCustomQueryTest( |
| const LensOverlayControllerStraightToSrpCustomQueryTest&) = delete; |
| void operator=(const LensOverlayControllerStraightToSrpCustomQueryTest&) = |
| delete; |
| |
| void SetUpFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {base::test::FeatureRefAndParams( |
| lens::features::kLensOverlayStraightToSrp, |
| {{"query", "use this query instead"}}), |
| base::test::FeatureRefAndParams( |
| lens::features::kLensOverlayEduActionChip, |
| {{"url-allow-filters", "[\"*\"]"}, |
| {"url-path-match-allow-filters", "[\"select\"]"}})}, |
| {}); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerStraightToSrpCustomQueryTest, |
| HomeworkActionChipOpensCsbResults) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId); |
| |
| const DeepQuery kPathToSidePanelSearchboxInput{ |
| "lens-side-panel-app", |
| "cr-searchbox", |
| "input", |
| }; |
| |
| // Helper function to check for specific text in an element. |
| auto CheckSearchboxValue = [](ui::ElementIdentifier web_contents_id, |
| const DeepQuery& query, |
| const std::string& expected_text) { |
| return CheckJsResultAt( |
| web_contents_id, query, |
| base::StringPrintf("el => el.value === '%s'", expected_text.c_str())); |
| }; |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| // Navigate to a matching page. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); |
| // We need to wait for paint in order to take a screenshot of the page. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->CompletedFirstVisuallyNonEmptyPaint(); |
| })); |
| |
| RunTestSequence( |
| PressButton(kLensOverlayHomeworkPageActionIconElementId), |
| // Side panel should open. |
| InAnyContext(InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId)), |
| |
| // The CSB query in the side panel should say "use this query instead" |
| InSameContext(CheckSearchboxValue(kOverlaySidePanelWebViewId, |
| kPathToSidePanelSearchboxInput, |
| "use this query instead"))); |
| } |
| |
| class LensOverlayControllerEduActionChipTest |
| : public LensOverlayControllerCUJTest { |
| public: |
| LensOverlayControllerEduActionChipTest() = default; |
| ~LensOverlayControllerEduActionChipTest() override = default; |
| LensOverlayControllerEduActionChipTest( |
| const LensOverlayControllerEduActionChipTest&) = delete; |
| void operator=(const LensOverlayControllerEduActionChipTest&) = delete; |
| |
| void SetUpFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {base::test::FeatureRefAndParams( |
| lens::features::kLensOverlayEduActionChip, |
| {{"url-allow-filters", "[\"*\"]"}, |
| {"url-path-match-allow-filters", "[\"select\"]"}, |
| {"max-shown-count", "5"}})}, |
| {lens::features::kLensOverlayStraightToSrp, |
| lens::features::kLensSearchZeroStateCsb}); |
| } |
| }; |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website that triggers the homework action chip. |
| // (2) User clicks the action chip and the overlay opens. The chip should hide. |
| // (3) User opens a new tab, then switches back. The chip should remain hidden. |
| // (4) User closes the overlay. |
| // (5) The chip should reshow. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerEduActionChipTest, |
| HomeworkActionChipHidesWhenOverlayOpen) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlayId); |
| |
| // In kDocumentWithNamedElement. |
| const DeepQuery kPathToBody{ |
| "body", |
| }; |
| |
| // In the lens overlay. |
| const DeepQuery kPathToCloseButton{ |
| "lens-overlay-app", |
| "#closeButton", |
| }; |
| constexpr char kClickFn[] = "(el) => { el.click(); }"; |
| |
| const GURL url = embedded_test_server()->GetURL(kDocumentWithNamedElement); |
| // Navigate to a matching page. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url))); |
| // We need to wait for paint in order to take a screenshot of the page. |
| ASSERT_TRUE(base::test::RunUntil([&]() { |
| return browser() |
| ->tab_strip_model() |
| ->GetActiveTab() |
| ->GetContents() |
| ->CompletedFirstVisuallyNonEmptyPaint(); |
| })); |
| |
| RunTestSequence( |
| // Ensure homework chip is visible. |
| EnsurePresent(kLensOverlayHomeworkPageActionIconElementId), |
| |
| PressButton(kLensOverlayHomeworkPageActionIconElementId), |
| |
| // 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( |
| InstrumentNonTabWebView(kOverlayId, |
| LensOverlayController::kOverlayId), |
| WaitForWebContentsReady( |
| kOverlayId, GURL(chrome::kChromeUILensOverlayUntrustedURL))), |
| |
| // Ensure homework chip is not visible after the overlay opens. |
| EnsureNotPresent(kLensOverlayHomeworkPageActionIconElementId), |
| |
| OpenArbitraryNewTab(), |
| |
| // Switch back to the original tab. |
| SelectTab(kTabStripElementId, 0), |
| |
| // Ensure homework chip is still not visible. |
| EnsureNotPresent(kLensOverlayHomeworkPageActionIconElementId), |
| |
| InSameContext(EnsurePresent(kOverlayId, kPathToCloseButton), |
| ExecuteJsAt(kOverlayId, kPathToCloseButton, kClickFn, |
| ExecuteJsMode::kFireAndForget), |
| WaitForHide(kOverlayId)), |
| |
| // Ensure homework chip is visible again. |
| EnsurePresent(kLensOverlayHomeworkPageActionIconElementId)); |
| } |
| |
| class LensOverlayControllerZeroStateCsbTest |
| : public LensOverlayControllerCUJTest { |
| public: |
| LensOverlayControllerZeroStateCsbTest() = default; |
| ~LensOverlayControllerZeroStateCsbTest() override = default; |
| LensOverlayControllerZeroStateCsbTest( |
| const LensOverlayControllerStraightToSrpTest&) = delete; |
| void operator=(const LensOverlayControllerZeroStateCsbTest&) = delete; |
| |
| void SetUpFeatureList() override { |
| feature_list_.InitWithFeaturesAndParameters( |
| {base::test::FeatureRefAndParams( |
| lens::features::kLensSearchZeroStateCsb, {})}, |
| {}); |
| } |
| }; |
| |
| // This tests the following CUJ: |
| // (1) User navigates to a website. |
| // (2) User opens lens overlay and the side panel opens with CSB results. |
| IN_PROC_BROWSER_TEST_F(LensOverlayControllerZeroStateCsbTest, |
| OpenLensOverlayOpensResults) { |
| WaitForTemplateURLServiceToLoad(); |
| DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kOverlaySidePanelWebViewId); |
| |
| const DeepQuery kPathToSidePanelSearchboxInput{ |
| "lens-side-panel-app", |
| "cr-searchbox", |
| "input", |
| }; |
| |
| // Helper function to check for specific text in an element. |
| auto CheckSearchboxValue = [](ui::ElementIdentifier web_contents_id, |
| const DeepQuery& query, |
| const std::string& expected_text) { |
| return CheckJsResultAt( |
| web_contents_id, query, |
| base::StringPrintf("el => el.value === '%s'", expected_text.c_str())); |
| }; |
| |
| RunTestSequence( |
| OpenLensOverlay(), |
| // Side panel should open. |
| InAnyContext(InstrumentNonTabWebView( |
| kOverlaySidePanelWebViewId, |
| LensOverlayController::kOverlaySidePanelWebViewId), |
| WaitForWebContentsReady(kOverlaySidePanelWebViewId)), |
| |
| // The CSB query in the side panel should be empty. |
| InSameContext(CheckSearchboxValue(kOverlaySidePanelWebViewId, |
| kPathToSidePanelSearchboxInput, ""))); |
| } |
| |
| } // namespace |