| // 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 "chrome/browser/ui/ash/picker/picker_client_impl.h" |
| |
| #include <functional> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/picker/picker_controller.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_future.h" |
| #include "chrome/browser/ash/app_list/search/test/test_ranker_manager.h" |
| #include "chrome/browser/ash/drive/drive_integration_service.h" |
| #include "chrome/browser/ash/drive/drivefs_test_support.h" |
| #include "chrome/browser/ash/fileapi/recent_model.h" |
| #include "chrome/browser/ash/fileapi/recent_model_factory.h" |
| #include "chrome/browser/ash/fileapi/test/fake_recent_source.h" |
| #include "chrome/browser/ash/input_method/editor_mediator_factory.h" |
| #include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/search_engines/template_url_service_factory.h" |
| #include "chrome/browser/ui/webui/ash/mako/mako_bubble_coordinator.h" |
| #include "chrome/common/extensions/api/file_manager_private.h" |
| #include "chrome/test/base/browser_with_test_window_test.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "chromeos/ash/components/disks/disk_mount_manager.h" |
| #include "chromeos/ash/components/disks/fake_disk_mount_manager.h" |
| #include "chromeos/ash/components/drivefs/fake_drivefs.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "components/bookmarks/browser/bookmark_model.h" |
| #include "components/bookmarks/test/bookmark_test_helpers.h" |
| #include "components/favicon/core/test/mock_favicon_service.h" |
| #include "components/favicon_base/favicon_types.h" |
| #include "components/history/core/browser/history_database_params.h" |
| #include "components/history/core/browser/history_service.h" |
| #include "components/history/core/test/test_history_database.h" |
| #include "components/user_manager/fake_user_manager.h" |
| #include "content/public/test/test_utils.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/ime/ash/ime_bridge.h" |
| #include "ui/base/ime/ash/input_method_ash.h" |
| #include "ui/base/ime/fake_text_input_client.h" |
| #include "ui/display/screen.h" |
| #include "ui/display/test/test_screen.h" |
| #include "ui/views/accessibility/ax_event_manager.h" |
| #include "ui/views/test/ax_event_counter.h" |
| |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::AnyNumber; |
| using ::testing::Contains; |
| using ::testing::Field; |
| using ::testing::IsEmpty; |
| using ::testing::IsSupersetOf; |
| using ::testing::NiceMock; |
| using ::testing::Not; |
| using ::testing::Property; |
| using ::testing::SizeIs; |
| using ::testing::UnorderedElementsAre; |
| using ::testing::VariantWith; |
| |
| using MockSearchResultsCallback = |
| testing::MockFunction<PickerClientImpl::CrosSearchResultsCallback>; |
| |
| namespace fmp = extensions::api::file_manager_private; |
| |
| class TestFaviconService : public favicon::MockFaviconService { |
| public: |
| TestFaviconService() = default; |
| TestFaviconService(const TestFaviconService&) = delete; |
| TestFaviconService& operator=(const TestFaviconService&) = delete; |
| ~TestFaviconService() override = default; |
| |
| // favicon::FaviconService: |
| base::CancelableTaskTracker::TaskId GetFaviconImageForPageURL( |
| const GURL& page_url, |
| favicon_base::FaviconImageCallback callback, |
| base::CancelableTaskTracker* tracker) override { |
| page_url_ = page_url; |
| std::move(callback).Run(favicon_base::FaviconImageResult()); |
| return {}; |
| } |
| |
| GURL page_url_; |
| }; |
| |
| bool CreateTestFile(const base::FilePath& path) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| if (!base::WriteFile(path, "test_file")) { |
| return false; |
| } |
| return true; |
| } |
| |
| std::unique_ptr<KeyedService> BuildTestHistoryService( |
| base::FilePath profile_path, |
| content::BrowserContext* context) { |
| auto service = std::make_unique<history::HistoryService>(); |
| service->Init(history::TestHistoryDatabaseParamsForPath(profile_path)); |
| return std::move(service); |
| } |
| |
| struct Volume { |
| fmp::VolumeType type; |
| std::vector<ash::RecentFile> files; |
| }; |
| |
| std::unique_ptr<KeyedService> BuildTestRecentModelFactory( |
| std::vector<Volume> volumes, |
| content::BrowserContext* /*context*/) { |
| std::vector<std::unique_ptr<ash::RecentSource>> sources; |
| for (Volume& volume : volumes) { |
| auto source = std::make_unique<ash::FakeRecentSource>(volume.type); |
| source->AddProducer(std::make_unique<ash::FileProducer>( |
| /*lag=*/base::Milliseconds(0), std::move(volume.files))); |
| sources.push_back(std::move(source)); |
| } |
| return ash::RecentModel::CreateForTest(std::move(sources)); |
| } |
| |
| std::unique_ptr<KeyedService> BuildTestDriveIntegrationService( |
| const base::FilePath& profile_path, |
| std::unique_ptr<drive::FakeDriveFsHelper>& fake_drivefs_helper, |
| content::BrowserContext* context) { |
| Profile* profile = Profile::FromBrowserContext(context); |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath mount_path = profile_path.Append("drivefs"); |
| static_cast<ash::disks::FakeDiskMountManager*>( |
| ash::disks::DiskMountManager::GetInstance()) |
| ->RegisterMountPointForNetworkStorageScheme("drivefs", |
| mount_path.value()); |
| fake_drivefs_helper = |
| std::make_unique<drive::FakeDriveFsHelper>(profile, mount_path); |
| auto service = std::make_unique<drive::DriveIntegrationService>( |
| profile, "drivefs", mount_path, |
| fake_drivefs_helper->CreateFakeDriveFsListenerFactory()); |
| |
| // Wait until the DriveIntegrationService is initialized. |
| while (!service->IsMounted() || !service->GetDriveFsInterface()) { |
| base::RunLoop().RunUntilIdle(); |
| } |
| return service; |
| } |
| |
| void AddSearchToHistory(TestingProfile* profile, |
| GURL url, |
| base::Time last_visit = base::Time::Now()) { |
| history::HistoryService* history = HistoryServiceFactory::GetForProfile( |
| profile, ServiceAccessType::EXPLICIT_ACCESS); |
| history->AddPageWithDetails(url, /*title=*/u"", /*visit_count=*/1, |
| /*typed_count=*/1, |
| /*last_visit=*/last_visit, |
| /*hidden=*/false, history::SOURCE_BROWSED); |
| profile->BlockUntilHistoryProcessesPendingRequests(); |
| } |
| |
| void AddBookmarks(TestingProfile* profile, |
| std::u16string_view title, |
| GURL url) { |
| auto* bookmark_model = BookmarkModelFactory::GetForBrowserContext(profile); |
| bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model); |
| |
| bookmark_model->AddURL(bookmark_model->bookmark_bar_node(), 0, |
| std::u16string(title), url); |
| } |
| |
| ash::RecentFile CreateRecentFile(const base::FilePath& file_path, |
| storage::FileSystemType type, |
| base::Time last_modified = base::Time::Now()) { |
| CreateTestFile(file_path); |
| return ash::RecentFile(storage::FileSystemURL::CreateForTest( |
| blink::StorageKey(), type, file_path), |
| last_modified); |
| } |
| |
| void SetRecentFiles(TestingProfile* profile, std::vector<Volume> volumes) { |
| ash::RecentModelFactory::GetInstance()->SetTestingFactoryAndUse( |
| profile, |
| base::BindRepeating(BuildTestRecentModelFactory, std::move(volumes))); |
| } |
| |
| class PickerClientImplTest : public BrowserWithTestWindowTest { |
| public: |
| PickerClientImplTest() = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| ash::CrosDisksClient::InitializeFake(); |
| ash::disks::DiskMountManager::InitializeForTesting( |
| new ash::disks::FakeDiskMountManager()); |
| |
| BrowserWithTestWindowTest::SetUp(); |
| } |
| void TearDown() override { |
| BrowserWithTestWindowTest::TearDown(); |
| |
| ash::disks::DiskMountManager::Shutdown(); |
| ash::CrosDisksClient::Shutdown(); |
| } |
| |
| scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory() { |
| return test_shared_url_loader_factory_; |
| } |
| |
| drivefs::FakeDriveFs& GetFakeDriveFs() { |
| return fake_drivefs_helper_->fake_drivefs(); |
| } |
| |
| TestingProfile* CreateProfile(const std::string& profile_name) override { |
| auto* profile = profile_manager()->CreateTestingProfile( |
| profile_name, GetTestingFactories(), /*is_main_profile=*/false, |
| test_shared_url_loader_factory_); |
| OnUserProfileCreated(profile_name, profile); |
| return profile; |
| } |
| |
| TestingProfile::TestingFactories GetTestingFactories() override { |
| return { |
| TestingProfile::TestingFactory{ |
| HistoryServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildTestHistoryService, temp_dir_.GetPath())}, |
| TestingProfile::TestingFactory{ |
| BookmarkModelFactory::GetInstance(), |
| BookmarkModelFactory::GetDefaultFactory()}, |
| TestingProfile::TestingFactory{ |
| TemplateURLServiceFactory::GetInstance(), |
| base::BindRepeating(&TemplateURLServiceFactory::BuildInstanceFor)}, |
| TestingProfile::TestingFactory{ |
| ash::RecentModelFactory::GetInstance(), |
| base::BindRepeating(&BuildTestRecentModelFactory, |
| std::vector<Volume>{})}, |
| TestingProfile::TestingFactory{ |
| drive::DriveIntegrationServiceFactory::GetInstance(), |
| base::BindRepeating(&BuildTestDriveIntegrationService, |
| temp_dir_.GetPath(), |
| std::ref(fake_drivefs_helper_))}, |
| TestingProfile::TestingFactory{ |
| ash::input_method::EditorMediatorFactory::GetInstance(), |
| base::BindRepeating( |
| &ash::input_method::EditorMediatorFactory::BuildInstanceFor)}}; |
| } |
| |
| void LogIn(const std::string& email) override { |
| // DriveFS needs the account to have an ID. |
| const AccountId account_id = |
| AccountId::FromUserEmailGaiaId(email, "test gaia"); |
| user_manager()->AddUser(account_id); |
| ash_test_helper()->test_session_controller_client()->AddUserSession(email); |
| user_manager()->UserLoggedIn( |
| account_id, |
| user_manager::FakeUserManager::GetFakeUsernameHash(account_id), |
| /*browser_restart=*/false, |
| /*is_child=*/false); |
| } |
| |
| private: |
| base::ScopedTempDir temp_dir_; |
| scoped_refptr<network::SharedURLLoaderFactory> |
| test_shared_url_loader_factory_; |
| std::unique_ptr<drive::FakeDriveFsHelper> fake_drivefs_helper_; |
| }; |
| |
| TEST_F(PickerClientImplTest, StartCrosSearch) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| AddSearchToHistory(profile(), GURL("http://foo.com/history")); |
| AddBookmarks(profile(), u"Foobaz", GURL("http://foo.com/bookmarks")); |
| AddTab(browser(), GURL("http://foo.com/tab")); |
| base::test::TestFuture<void> test_done; |
| |
| auto ranker_manager = |
| std::make_unique<app_list::TestRankerManager>(profile()); |
| ranker_manager->SetBestMatchString(u"tab"); |
| client.set_ranker_manager_for_test(std::move(ranker_manager)); |
| |
| NiceMock<MockSearchResultsCallback> mock_search_callback; |
| EXPECT_CALL(mock_search_callback, Call(_, _)).Times(AnyNumber()); |
| EXPECT_CALL( |
| mock_search_callback, |
| Call( |
| ash::AppListSearchResultType::kOmnibox, |
| IsSupersetOf({ |
| VariantWith<ash::PickerBrowsingHistoryResult>(AllOf( |
| Field("url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://foo.com/history")), |
| Field("best_match", |
| &ash::PickerBrowsingHistoryResult::best_match, false))), |
| VariantWith<ash::PickerBrowsingHistoryResult>(AllOf( |
| Field("url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://foo.com/tab")), |
| Field("best_match", |
| &ash::PickerBrowsingHistoryResult::best_match, true))), |
| |
| VariantWith<ash::PickerBrowsingHistoryResult>(AllOf( |
| Field("title", &ash::PickerBrowsingHistoryResult::title, |
| u"Foobaz"), |
| Field("url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://foo.com/bookmarks")), |
| Field("best_match", |
| &ash::PickerBrowsingHistoryResult::best_match, false))), |
| }))) |
| .WillOnce([&]() { test_done.SetValue(); }); |
| |
| client.StartCrosSearch( |
| u"foo", /*category=*/std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&mock_search_callback))); |
| |
| ASSERT_TRUE(test_done.Wait()); |
| } |
| |
| TEST_F(PickerClientImplTest, IgnoresWhatYouTypedResults) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<void> test_done; |
| |
| NiceMock<MockSearchResultsCallback> mock_search_callback; |
| EXPECT_CALL(mock_search_callback, Call(_, _)).Times(AnyNumber()); |
| EXPECT_CALL(mock_search_callback, |
| Call(ash::AppListSearchResultType::kOmnibox, IsEmpty())) |
| .WillOnce([&]() { test_done.SetValue(); }); |
| |
| client.StartCrosSearch( |
| u"a.com", /*category=*/std::nullopt, |
| base::BindRepeating(&MockSearchResultsCallback::Call, |
| base::Unretained(&mock_search_callback))); |
| |
| ASSERT_TRUE(test_done.Wait()); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentLocalFilesWithNoFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| |
| client.GetRecentLocalFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), IsEmpty()); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentLocalFilesReturnsOnlyLocalFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| const base::FilePath mount_path = GetFakeDriveFs().mount_path(); |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDownloads, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("local.png"), |
| storage::kFileSystemTypeLocal), |
| }, |
| }, |
| Volume{ |
| .type = fmp::VolumeType::kDrive, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("drive.png"), |
| storage::kFileSystemTypeDriveFs), |
| }, |
| }, |
| }); |
| |
| client.GetRecentLocalFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT( |
| future.Get(), |
| UnorderedElementsAre(VariantWith<ash::PickerLocalFileResult>( |
| Field("title", &ash::PickerLocalFileResult::title, u"local.png")))); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentLocalFilesDoesNotReturnOldFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDownloads, |
| .files = |
| { |
| CreateRecentFile( |
| GetFakeDriveFs().mount_path().AppendASCII("old.png"), |
| storage::kFileSystemTypeLocal, |
| base::Time::Now() - base::Days(31)), |
| }, |
| }, |
| }); |
| |
| client.GetRecentLocalFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), IsEmpty()); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentDriveFilesWithNoFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| |
| client.GetRecentDriveFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), IsEmpty()); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentDriveFilesReturnsOnlyDriveFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| const base::FilePath mount_path = GetFakeDriveFs().mount_path(); |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDownloads, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("local.png"), |
| storage::kFileSystemTypeLocal), |
| }, |
| }, |
| Volume{ |
| .type = fmp::VolumeType::kDrive, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("drive.png"), |
| storage::kFileSystemTypeDriveFs), |
| }, |
| }, |
| }); |
| |
| client.GetRecentDriveFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT( |
| future.Get(), |
| UnorderedElementsAre(VariantWith<ash::PickerDriveFileResult>(AllOf( |
| Field("title", &ash::PickerDriveFileResult::title, u"drive.png"), |
| Field("url", &ash::PickerDriveFileResult::url, |
| GURL("https://file_alternate_link/drive.png")))))); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentDriveFilesDoesNotReturnOldFiles) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDrive, |
| .files = |
| { |
| CreateRecentFile( |
| GetFakeDriveFs().mount_path().AppendASCII("old.png"), |
| storage::kFileSystemTypeDriveFs, |
| base::Time::Now() - base::Days(31)), |
| }, |
| }, |
| }); |
| |
| client.GetRecentLocalFileResults(/*max_files=*/100, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), IsEmpty()); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentLocalFilesTruncates) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| const base::FilePath mount_path = GetFakeDriveFs().mount_path(); |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDownloads, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("1.jpg"), |
| storage::kFileSystemTypeLocal), |
| CreateRecentFile(mount_path.AppendASCII("2.jpg"), |
| storage::kFileSystemTypeLocal), |
| }, |
| }, |
| }); |
| |
| client.GetRecentLocalFileResults(/*max_files=*/1, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), SizeIs(1)); |
| } |
| |
| TEST_F(PickerClientImplTest, GetRecentDriveFilesTruncates) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| const base::FilePath mount_path = GetFakeDriveFs().mount_path(); |
| SetRecentFiles( |
| profile(), |
| { |
| Volume{ |
| .type = fmp::VolumeType::kDrive, |
| .files = |
| { |
| CreateRecentFile(mount_path.AppendASCII("1"), |
| storage::kFileSystemTypeDriveFs), |
| CreateRecentFile(mount_path.AppendASCII("2"), |
| storage::kFileSystemTypeDriveFs), |
| }, |
| }, |
| }); |
| |
| client.GetRecentDriveFileResults(/*max_files=*/1, future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), SizeIs(1)); |
| } |
| |
| TEST_F(PickerClientImplTest, GetSuggestedLinkResultsReturnsLinks) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| const base::Time now = base::Time::Now(); |
| AddSearchToHistory(profile(), GURL("http://a.com/history"), |
| now - base::Seconds(1)); |
| AddSearchToHistory(profile(), GURL("http://b.com/history"), now); |
| TestFaviconService favicon_service; |
| client.get_link_suggester_for_test()->set_favicon_service_for_test( |
| &favicon_service); |
| |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| client.GetSuggestedLinkResults(100u, future.GetRepeatingCallback()); |
| |
| EXPECT_THAT(future.Get(), |
| ElementsAre(VariantWith<ash::PickerBrowsingHistoryResult>(Field( |
| "url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://b.com/history"))), |
| VariantWith<ash::PickerBrowsingHistoryResult>(Field( |
| "url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://a.com/history"))))); |
| EXPECT_EQ(favicon_service.page_url_, GURL("http://a.com/history")); |
| } |
| |
| TEST_F(PickerClientImplTest, GetSuggestedLinkResultsAreTruncatedToMostRecent) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| const base::Time now = base::Time::Now(); |
| AddSearchToHistory(profile(), GURL("http://a.com/history"), |
| now - base::Seconds(1)); |
| AddSearchToHistory(profile(), GURL("http://b.com/history"), now); |
| TestFaviconService favicon_service; |
| client.get_link_suggester_for_test()->set_favicon_service_for_test( |
| &favicon_service); |
| |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| client.GetSuggestedLinkResults(1u, future.GetRepeatingCallback()); |
| |
| EXPECT_THAT(future.Get(), |
| ElementsAre(VariantWith<ash::PickerBrowsingHistoryResult>( |
| Field("url", &ash::PickerBrowsingHistoryResult::url, |
| GURL("http://b.com/history"))))); |
| EXPECT_EQ(favicon_service.page_url_, GURL("http://b.com/history")); |
| } |
| |
| class PickerClientImplEditorTest : public PickerClientImplTest { |
| public: |
| ash::input_method::EditorMediator& GetEditorMediator(Profile* profile) { |
| return *ash::input_method::EditorMediatorFactory::GetForProfile(profile); |
| } |
| |
| ui::InputMethod& ime() { return ime_; } |
| |
| protected: |
| void SetUp() override { |
| PickerClientImplTest::SetUp(); |
| |
| ash::IMEBridge::Get()->SetInputContextHandler(&ime_); |
| } |
| |
| void TearDown() override { |
| PickerClientImplTest::TearDown(); |
| |
| ash::IMEBridge::Get()->SetInputContextHandler(nullptr); |
| } |
| |
| private: |
| ash::InputMethodAsh ime_{nullptr}; |
| }; |
| |
| TEST_F(PickerClientImplEditorTest, |
| IsEligibleForEditorReturnsFalseIfEditorDisabled) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kHardBlocked); |
| |
| EXPECT_FALSE(client.IsEligibleForEditor()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| IsEligibleForEditorReturnsFalseIfHardBlocked) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kHardBlocked); |
| |
| EXPECT_FALSE(client.IsEligibleForEditor()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| IsEligibleForEditorReturnsTrueIfSoftBlocked) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kSoftBlocked); |
| |
| EXPECT_TRUE(client.IsEligibleForEditor()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| CacheEditorContextReturnsNullCallbackWhenEditorFlagDisabled) { |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kHardBlocked); |
| |
| EXPECT_TRUE(client.CacheEditorContext().is_null()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| CacheEditorContextReturnsNullCallbackWhenBlocked) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kSoftBlocked); |
| |
| EXPECT_TRUE(client.CacheEditorContext().is_null()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| CacheEditorContextReturnsCallbackWhenNotBlocked) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kConsentNeeded); |
| |
| EXPECT_FALSE(client.CacheEditorContext().is_null()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, CacheEditorContextCachesCaretBounds) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kConsentNeeded); |
| ui::FakeTextInputClient text_input_client( |
| &ime(), { |
| .type = ui::TEXT_INPUT_TYPE_TEXT, |
| .caret_bounds = gfx::Rect(1, 2, 3, 4), |
| }); |
| text_input_client.Focus(); |
| |
| client.CacheEditorContext(); |
| |
| EXPECT_EQ(GetEditorMediator(profile()) |
| .mako_bubble_coordinator_for_testing() |
| .context_caret_bounds_for_testing(), |
| gfx::Rect(1, 2, 3, 4)); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, GetSuggestedEditorResults) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kRewrite); |
| ui::FakeTextInputClient text_input_client(&ime(), |
| {.type = ui::TEXT_INPUT_TYPE_TEXT}); |
| text_input_client.Focus(); |
| |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| client.GetSuggestedEditorResults(future.GetCallback()); |
| |
| EXPECT_TRUE(future.Wait()); |
| // TODO: b/331286774 - Add expectation for the suggested editor results once |
| // EditorServiceConnector is injectable. |
| } |
| |
| TEST_F(PickerClientImplEditorTest, |
| GetSuggestedEditorResultsReturnsNothingWhenBlocked) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| GetEditorMediator(profile()).OverrideEditorModeForTesting( |
| ash::input_method::EditorMode::kSoftBlocked); |
| ui::FakeTextInputClient text_input_client(&ime(), |
| {.type = ui::TEXT_INPUT_TYPE_TEXT}); |
| text_input_client.Focus(); |
| |
| base::test::TestFuture<std::vector<ash::PickerSearchResult>> future; |
| client.GetSuggestedEditorResults(future.GetCallback()); |
| |
| EXPECT_THAT(future.Get(), IsEmpty()); |
| } |
| |
| TEST_F(PickerClientImplEditorTest, AnnounceSendsLiveRegionChanges) { |
| base::test::ScopedFeatureList features(chromeos::features::kOrcaDogfood); |
| ash::PickerController controller; |
| PickerClientImpl client(&controller, user_manager()); |
| views::test::AXEventCounter counter(views::AXEventManager::Get()); |
| |
| client.Announce(u"hello"); |
| |
| counter.WaitForEvent(ax::mojom::Event::kLiveRegionChanged); |
| } |
| |
| // TODO: b/325540366 - Add PickerClientImpl tests. |
| |
| } // namespace |