// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/picker/views/picker_section_view.h"
#include <memory>
#include <string>
#include <utility>
#include "ash/picker/mock_picker_asset_fetcher.h"
#include "ash/picker/views/picker_gif_view.h"
#include "ash/picker/views/picker_image_item_view.h"
#include "ash/picker/views/picker_item_view.h"
#include "ash/picker/views/picker_list_item_view.h"
#include "ash/picker/views/picker_preview_bubble_controller.h"
#include "ash/picker/views/picker_submenu_controller.h"
#include "ash/public/cpp/picker/picker_search_result.h"
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "components/vector_icons/vector_icons.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/view.h"
#include "ui/views/view_utils.h"
#include "url/gurl.h"
namespace ash {
namespace {
using ::testing::IsEmpty;
using ::testing::Property;
using ::testing::SizeIs;
using ::testing::StrEq;
constexpr int kDefaultSectionWidth = 320;
std::unique_ptr<PickerImageItemView> CreateGifItem(
const gfx::Size& gif_dimensions) {
return std::make_unique<PickerImageItemView>(
base::DoNothing(),
std::make_unique<PickerGifView>(
/*frames_fetcher=*/base::DoNothing(),
/*preview_image_fetcher=*/base::DoNothing(), gif_dimensions,
/*accessible_name=*/u""));
}
using PickerSectionViewTest = views::ViewsTestBase;
TEST_F(PickerSectionViewTest, HasListRole) {
PickerSectionView section_view(kDefaultSectionWidth,
/*asset_fetcher=*/nullptr,
/*submenu_controller=*/nullptr);
EXPECT_EQ(section_view.GetAccessibleRole(), ax::mojom::Role::kList);
}
TEST_F(PickerSectionViewTest, CreatesTitleLabel) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
const std::u16string kSectionTitleText = u"Section";
section_view.AddTitleLabel(kSectionTitleText);
EXPECT_THAT(section_view.title_label_for_testing(),
Property(&views::Label::GetText, kSectionTitleText));
}
TEST_F(PickerSectionViewTest, TitleHasHeadingRole) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddTitleLabel(u"Section");
EXPECT_THAT(section_view.title_label_for_testing()->GetAccessibleRole(),
ax::mojom::Role::kHeading);
}
TEST_F(PickerSectionViewTest, AddsListItem) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddListItem(
std::make_unique<PickerListItemView>(base::DoNothing()));
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[0]));
}
TEST_F(PickerSectionViewTest, AddsTwoListItems) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddListItem(
std::make_unique<PickerListItemView>(base::DoNothing()));
section_view.AddListItem(
std::make_unique<PickerListItemView>(base::DoNothing()));
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(2));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[0]));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[1]));
}
TEST_F(PickerSectionViewTest, AddsGifItem) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddImageGridItem(CreateGifItem(gfx::Size(100, 100)));
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
EXPECT_TRUE(views::IsViewClass<PickerImageItemView>(items[0]));
}
TEST_F(PickerSectionViewTest, AddsResults) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(PickerTextResult(u"Result"), &preview_controller,
PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
section_view.AddResult(
PickerLocalFileResult(u"title", base::FilePath("abc.png")),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(2));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[0]));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[1]));
}
TEST_F(PickerSectionViewTest,
BrowsingHistoryResultsWithTitleShowsTitleAsPrimary) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerBrowsingHistoryResult(GURL("https://www.example.com/foo"),
u"Example Foo", /*icon=*/{}),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
auto* list_item = views::AsViewClass<PickerListItemView>(items[0]);
ASSERT_TRUE(list_item);
EXPECT_EQ(list_item->GetPrimaryTextForTesting(), u"Example Foo");
EXPECT_EQ(list_item->GetSecondaryTextForTesting(), u"example.com/foo");
}
TEST_F(PickerSectionViewTest,
BrowsingHistoryResultsWithoutTitleShowsUrlAsPrimary) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerBrowsingHistoryResult(GURL("https://www.example.com/foo"),
/*title=*/u"", /*icon=*/{}),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
auto* list_item = views::AsViewClass<PickerListItemView>(items[0]);
ASSERT_TRUE(list_item);
EXPECT_EQ(list_item->GetPrimaryTextForTesting(), u"example.com/foo");
EXPECT_EQ(list_item->GetSecondaryTextForTesting(), u"example.com/foo");
}
TEST_F(PickerSectionViewTest,
SingleFileClipboardHistoryResultsUseIconForFiletype) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerClipboardResult(base::UnguessableToken(),
PickerClipboardResult::DisplayFormat::kFile,
/*file_count=*/1,
/*display_text=*/u"image.png",
/*display_image=*/{},
/*is_recent=*/false),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
auto* list_item = views::AsViewClass<PickerListItemView>(items[0]);
ASSERT_NE(list_item, nullptr);
const gfx::VectorIcon* vector_icon =
list_item->leading_icon_view_for_testing()
.GetImageModel()
.GetVectorIcon()
.vector_icon();
ASSERT_NE(vector_icon, nullptr);
EXPECT_THAT(vector_icon->name, StrEq(chromeos::kFiletypeImageIcon.name));
}
TEST_F(PickerSectionViewTest,
MultipleFileClipboardHistoryResultsUseIconForFiletype) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerClipboardResult(base::UnguessableToken(),
PickerClipboardResult::DisplayFormat::kFile,
/*file_count=*/2,
/*display_text=*/u"2 files",
/*display_image=*/{},
/*is_recent=*/false),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
auto* list_item = views::AsViewClass<PickerListItemView>(items[0]);
ASSERT_NE(list_item, nullptr);
const gfx::VectorIcon* vector_icon =
list_item->leading_icon_view_for_testing()
.GetImageModel()
.GetVectorIcon()
.vector_icon();
ASSERT_NE(vector_icon, nullptr);
EXPECT_THAT(vector_icon->name, StrEq(vector_icons::kContentCopyIcon.name));
}
TEST_F(PickerSectionViewTest, CapsLockResultShowsShortcutHint) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerCapsLockResult(
/*enabled=*/true, PickerCapsLockResult::Shortcut::kAltSearch),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
auto* list_item = views::AsViewClass<PickerListItemView>(items[0]);
ASSERT_TRUE(list_item);
EXPECT_NE(list_item->shortcut_hint_view_for_testing(), nullptr);
}
TEST_F(PickerSectionViewTest, ClearsItems) {
MockPickerAssetFetcher asset_fetcher;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddListItem(
std::make_unique<PickerListItemView>(base::DoNothing()));
section_view.ClearItems();
EXPECT_THAT(section_view.item_views_for_testing(), IsEmpty());
}
class PickerSectionViewUrlFormattingTest
: public PickerSectionViewTest,
public testing::WithParamInterface<std::pair<GURL, std::u16string>> {};
INSTANTIATE_TEST_SUITE_P(
,
PickerSectionViewUrlFormattingTest,
testing::Values(
std::make_pair(GURL("http://foo.com/bar"), u"foo.com/bar"),
std::make_pair(GURL("https://foo.com/bar"), u"foo.com/bar"),
std::make_pair(GURL("https://www.foo.com/bar"), u"foo.com/bar"),
std::make_pair(GURL("chrome://version"), u"chrome://version"),
std::make_pair(GURL("chrome-extension://aaa"),
u"chrome-extension://aaa"),
std::make_pair(GURL("file://a/b/c"), u"file://a/b/c")));
TEST_P(PickerSectionViewUrlFormattingTest, AddingHistoryResultFormatsUrl) {
MockPickerAssetFetcher asset_fetcher;
PickerPreviewBubbleController preview_controller;
PickerSubmenuController submenu_controller;
PickerSectionView section_view(kDefaultSectionWidth, &asset_fetcher,
&submenu_controller);
section_view.AddResult(
PickerBrowsingHistoryResult(GetParam().first, u"title", /*icon=*/{}),
&preview_controller, PickerSectionView::LocalFileResultStyle::kList,
base::DoNothing());
base::span<const raw_ptr<PickerItemView>> items =
section_view.item_views_for_testing();
ASSERT_THAT(items, SizeIs(1));
EXPECT_TRUE(views::IsViewClass<PickerListItemView>(items[0]));
EXPECT_EQ(views::AsViewClass<PickerListItemView>(items[0])
->GetSecondaryTextForTesting(),
GetParam().second);
}
} // namespace
} // namespace ash