chromium/chrome/browser/ui/ash/clipboard/clipboard_history_interactive_uitest.cc

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

#include <list>

#include "ash/clipboard/clipboard_history_controller_impl.h"
#include "ash/clipboard/clipboard_history_menu_model_adapter.h"
#include "ash/shell.h"
#include "base/metrics/histogram_base.h"
#include "base/path_service.h"
#include "chrome/browser/ui/ash/clipboard/clipboard_history_test_util.h"
#include "chrome/browser/ui/ash/clipboard/clipboard_image_model_request.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "ui/events/test/event_generator.h"

namespace {
using ImageModelRequestTestParams = ClipboardImageModelRequest::TestParams;

ash::ClipboardHistoryControllerImpl* GetClipboardHistoryController() {
  return ash::Shell::Get()->clipboard_history_controller();
}

ash::ClipboardHistoryMenuModelAdapter* GetContextMenu() {
  return GetClipboardHistoryController()->context_menu_for_test();
}

const std::list<ash::ClipboardHistoryItem>& GetClipboardItems() {
  return GetClipboardHistoryController()->history()->GetItems();
}

}  // namespace

// Interactive UI tests are used rather than browser tests in cases where window
// capture changes caused by other browser tests running in parallel could cause
// tests to fail, e.g., because the clipboard history menu closes.
class ClipboardHistoryWebContentsInteractiveTest : public InProcessBrowserTest {
 public:
  // InProcessBrowserTest:
  void SetUpOnMainThread() override {
    InProcessBrowserTest::SetUpOnMainThread();
    base::FilePath test_data_dir;
    ASSERT_TRUE(
        base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_data_dir));
    host_resolver()->AddRule("*", "127.0.0.1");
    embedded_test_server()->ServeFilesFromDirectory(
        test_data_dir.AppendASCII("chrome/test/data/ash/clipboard_history"));
    ASSERT_TRUE(embedded_test_server()->Start());
  }
};

// Verifies that the images rendered from the copied web contents show in the
// clipboard history menu. This test case covers switching auto-resize mode.
IN_PROC_BROWSER_TEST_F(ClipboardHistoryWebContentsInteractiveTest,
                       VerifyHTMLRendering) {
  // Load the web page which contains images and text.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/image-and-text.html")));

  // Select one part of the web page. Wait until the selection region updates.
  // Then copy the selected part to clipboard.
  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::BoundingBoxUpdateWaiter select_part_one(web_contents);
  ASSERT_TRUE(ExecJs(web_contents, "selectPart1();"));
  select_part_one.Wait();

  const auto& item_lists = GetClipboardItems();
  {
    clipboard_history::ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
    ASSERT_TRUE(ExecJs(web_contents, "copyToClipboard();"));
  }
  ASSERT_EQ(1u, item_lists.size());

  base::HistogramTester histogram_tester;

  // Show the clipboard history menu through the accelerator. When the clipboard
  // history menu shows, the process of HTML rendering starts.
  auto event_generator = std::make_unique<ui::test::EventGenerator>(
      ash::Shell::GetPrimaryRootWindow());
  event_generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_V,
                                                     ui::EF_COMMAND_DOWN);

  // Render HTML with auto-resize mode enabled. Wait until the rendering
  // finishes.
  ImageModelRequestTestParams test_params(
      /*callback=*/base::NullCallback(),
      /*enforce_auto_resize=*/true);
  {
    clipboard_history::ClipboardImageModelRequestWaiter image_request_waiter(
        &test_params, /*expect_auto_resize=*/true);
    image_request_waiter.Wait();
  }

  // Verify that the rendering ends normally.
  histogram_tester.ExpectUniqueSample(
      "Ash.ClipboardHistory.ImageModelRequest.StopReason",
      static_cast<int>(
          ClipboardImageModelRequest::RequestStopReason::kFulfilled),
      1);

  // Verify that the clipboard history menu shows. Then close the menu.
  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
  event_generator->PressAndReleaseKey(ui::VKEY_ESCAPE, ui::EF_NONE);
  EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());

  // Select another part. Wait until the selection region updates. Then copy
  // the selected HTML code to clipboard.
  content::BoundingBoxUpdateWaiter select_part_two(web_contents);
  ASSERT_TRUE(ExecJs(web_contents, "selectPart2();"));
  select_part_two.Wait();

  {
    clipboard_history::ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
    ASSERT_TRUE(ExecJs(web_contents, "copyToClipboard();"));
  }
  ASSERT_EQ(2u, item_lists.size());

  // Show the clipboard history menu.
  event_generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_V,
                                                     ui::EF_COMMAND_DOWN);

  // Render HTML with auto-resize mode disabled. Wait until the rendering
  // finishes.
  test_params.enforce_auto_resize = false;
  {
    clipboard_history::ClipboardImageModelRequestWaiter image_request_waiter(
        &test_params, /*expect_auto_resize=*/false);
    image_request_waiter.Wait();
  }

  // Verify that the rendering ends normally.
  histogram_tester.ExpectUniqueSample(
      "Ash.ClipboardHistory.ImageModelRequest.StopReason",
      static_cast<int>(
          ClipboardImageModelRequest::RequestStopReason::kFulfilled),
      2);

  // Verify that the clipboard history menu is open and populated with the
  // correct number of items.
  EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
  ASSERT_EQ(2u, GetContextMenu()->GetMenuItemsCount());
}

// Verifies that the clipboard history menu works as expected when copying a
// large web page.
IN_PROC_BROWSER_TEST_F(ClipboardHistoryWebContentsInteractiveTest,
                       CopyLargeWebPage) {
  // Load the web page which contains images and text.
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(), embedded_test_server()->GetURL("/large-image-page.html")));

  // Select one part of the web page. Wait until the selection region updates.
  // Then copy the selected part to clipboard.
  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
  content::BoundingBoxUpdateWaiter select_part_one(web_contents);
  ASSERT_TRUE(ExecJs(web_contents, "selectPart1();"));
  select_part_one.Wait();

  const auto& item_lists = GetClipboardItems();
  {
    clipboard_history::ScopedClipboardHistoryListUpdateWaiter scoped_waiter;
    ASSERT_TRUE(ExecJs(web_contents, "copyToClipboard();"));
  }
  ASSERT_EQ(1u, item_lists.size());

  base::HistogramTester histogram_tester;

  // Show the clipboard history menu through the accelerator. When the clipboard
  // history menu shows, the process of HTML rendering starts.
  auto event_generator = std::make_unique<ui::test::EventGenerator>(
      ash::Shell::GetPrimaryRootWindow());
  event_generator->PressAndReleaseKeyAndModifierKeys(ui::VKEY_V,
                                                     ui::EF_COMMAND_DOWN);

  ImageModelRequestTestParams test_params(
      /*callback=*/base::NullCallback());

  // Wait until the rendering finishes. Check that the web page is rendered
  // in auto-resize mode because the original web page's size is too big.
  clipboard_history::ClipboardImageModelRequestWaiter image_request_waiter(
      &test_params, /*expect_auto_resize=*/true);
  image_request_waiter.Wait();

  // Verify that the rendering ends normally.
  histogram_tester.ExpectUniqueSample(
      "Ash.ClipboardHistory.ImageModelRequest.StopReason",
      static_cast<int>(
          ClipboardImageModelRequest::RequestStopReason::kFulfilled),
      1);
}