| // Copyright 2021 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/download/bubble/download_bubble_ui_controller.h" |
| |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "chrome/browser/download/bubble/download_bubble_prefs.h" |
| #include "chrome/browser/download/bubble/download_bubble_update_service.h" |
| #include "chrome/browser/download/bubble/download_display_controller.h" |
| #include "chrome/browser/download/chrome_download_manager_delegate.h" |
| #include "chrome/browser/download/download_core_service.h" |
| #include "chrome/browser/download/download_core_service_factory.h" |
| #include "chrome/browser/download/download_item_model.h" |
| #include "chrome/browser/download/download_prefs.h" |
| #include "chrome/browser/download/offline_item_model_manager_factory.h" |
| #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" |
| #include "chrome/browser/profiles/profile_key.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/download/download_display.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/test/base/test_browser_window.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 "components/download/public/common/download_danger_type.h" |
| #include "components/download/public/common/download_item.h" |
| #include "components/download/public/common/mock_download_item.h" |
| #include "components/offline_items_collection/core/offline_item.h" |
| #include "components/offline_items_collection/core/offline_item_state.h" |
| #include "components/offline_items_collection/core/test_support/mock_offline_content_provider.h" |
| #include "content/public/browser/download_item_utils.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/mock_download_manager.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| using ::offline_items_collection::OfflineItemState; |
| using ::testing::_; |
| using ::testing::NiceMock; |
| using ::testing::Return; |
| using ::testing::ReturnRef; |
| using ::testing::ReturnRefOfCopy; |
| using ::testing::SetArgPointee; |
| using ::testing::StrictMock; |
| using StrictMockDownloadItem = testing::StrictMock<download::MockDownloadItem>; |
| using DownloadDangerType = download::DownloadDangerType; |
| using DownloadState = download::DownloadItem::DownloadState; |
| using DownloadUIModelPtr = DownloadUIModel::DownloadUIModelPtr; |
| using OfflineItemList = |
| offline_items_collection::OfflineContentProvider::OfflineItemList; |
| |
| const char kProviderNamespace[] = "mock_namespace"; |
| |
| class MockDownloadDisplayController : public DownloadDisplayController { |
| public: |
| MockDownloadDisplayController(Browser* browser, |
| DownloadBubbleUIController* bubble_controller) |
| : DownloadDisplayController(nullptr, browser, bubble_controller) { |
| bubble_controller->SetDownloadDisplayController(this); |
| } |
| void MaybeShowButtonWhenCreated() override {} |
| MOCK_METHOD1(OnNewItem, void(bool)); |
| MOCK_METHOD2(OnUpdatedItem, void(bool, bool)); |
| MOCK_METHOD1(OnRemovedItem, void(const ContentId&)); |
| }; |
| |
| class MockDownloadBubbleUpdateService : public DownloadBubbleUpdateService { |
| public: |
| enum class ModelType { |
| kDownloadItem, |
| kOfflineItem, |
| }; |
| |
| MockDownloadBubbleUpdateService( |
| Profile* profile, |
| const std::vector<std::unique_ptr<StrictMockDownloadItem>>& |
| download_items, |
| const OfflineItemList& offline_items) |
| : DownloadBubbleUpdateService(profile), |
| profile_(profile), |
| download_items_(download_items), |
| offline_items_(offline_items) {} |
| MockDownloadBubbleUpdateService(const MockDownloadBubbleUpdateService&) = |
| delete; |
| MockDownloadBubbleUpdateService& operator=( |
| const MockDownloadBubbleUpdateService&) = delete; |
| |
| ~MockDownloadBubbleUpdateService() override = default; |
| |
| bool GetAllModelsToDisplay( |
| std::vector<DownloadUIModelPtr>& models, |
| const webapps::AppId* web_app_id, |
| bool force_backfill_download_items = true) override { |
| models.clear(); |
| int download_item_index = 0, offline_item_index = 0; |
| // Compose a list of models from the items stored in the test fixture. |
| for (ModelType type : model_types_) { |
| if (type == ModelType::kDownloadItem) { |
| auto model = DownloadItemModel::Wrap( |
| download_items_->at(download_item_index++).get()); |
| if (model->ShouldShowInBubble()) { |
| models.push_back(std::move(model)); |
| } |
| } else { |
| auto model = OfflineItemModel::Wrap( |
| OfflineItemModelManagerFactory::GetForBrowserContext(profile_), |
| offline_items_->at(offline_item_index++)); |
| if (model->ShouldShowInBubble()) { |
| models.push_back(std::move(model)); |
| } |
| } |
| } |
| return true; |
| } |
| |
| void AddModel(ModelType type) { model_types_.push_back(type); } |
| |
| bool IsInitialized() const override { return true; } |
| |
| MOCK_METHOD(DownloadDisplay::ProgressInfo, |
| GetProgressInfo, |
| (const webapps::AppId*), |
| (const override)); |
| |
| private: |
| raw_ptr<Profile> profile_; |
| std::vector<ModelType> model_types_; |
| const raw_ref<const std::vector<std::unique_ptr<StrictMockDownloadItem>>> |
| download_items_; |
| const raw_ref<const OfflineItemList> offline_items_; |
| }; |
| |
| class DownloadBubbleUIControllerTest : public testing::Test { |
| public: |
| DownloadBubbleUIControllerTest() |
| : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) {} |
| DownloadBubbleUIControllerTest(const DownloadBubbleUIControllerTest&) = |
| delete; |
| DownloadBubbleUIControllerTest& operator=( |
| const DownloadBubbleUIControllerTest&) = delete; |
| |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kNoFirstRun); |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| |
| profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile"); |
| auto manager = std::make_unique<NiceMock<content::MockDownloadManager>>(); |
| manager_ = manager.get(); |
| EXPECT_CALL(*manager, GetBrowserContext()) |
| .WillRepeatedly(Return(profile_.get())); |
| EXPECT_CALL(*manager, RemoveObserver(_)).WillRepeatedly(Return()); |
| profile_->SetDownloadManagerForTesting(std::move(manager)); |
| |
| // Set test delegate to get the corresponding download prefs. |
| auto delegate = std::make_unique<ChromeDownloadManagerDelegate>(profile_); |
| DownloadCoreServiceFactory::GetForBrowserContext(profile_) |
| ->SetDownloadManagerDelegateForTesting(std::move(delegate)); |
| |
| content_provider_ = std::make_unique< |
| NiceMock<offline_items_collection::MockOfflineContentProvider>>(); |
| OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey()) |
| ->RegisterProvider(kProviderNamespace, content_provider_.get()); |
| |
| mock_update_service_ = |
| std::make_unique<StrictMock<MockDownloadBubbleUpdateService>>( |
| profile_, items_, offline_items_); |
| auto window = std::make_unique<TestBrowserWindow>(); |
| Browser::CreateParams params(profile_, true); |
| params.type = Browser::TYPE_NORMAL; |
| params.window = window.release(); |
| browser_ = Browser::DeprecatedCreateOwnedForTesting(params); |
| controller_ = std::make_unique<DownloadBubbleUIController>( |
| browser_.get(), mock_update_service_.get()); |
| display_controller_ = |
| std::make_unique<NiceMock<MockDownloadDisplayController>>( |
| browser_.get(), controller_.get()); |
| second_controller_ = std::make_unique<DownloadBubbleUIController>( |
| browser_.get(), mock_update_service_.get()); |
| second_display_controller_ = |
| std::make_unique<NiceMock<MockDownloadDisplayController>>( |
| browser_.get(), second_controller_.get()); |
| } |
| |
| void TearDown() override { |
| DownloadCoreServiceFactory::GetForBrowserContext(profile_) |
| ->SetDownloadManagerDelegateForTesting(nullptr); |
| // The controller needs to be reset before download manager, because the |
| // download_notifier_ will unregister itself from the manager. |
| controller_.reset(); |
| second_controller_.reset(); |
| display_controller_.reset(); |
| second_display_controller_.reset(); |
| mock_update_service_.reset(); |
| } |
| |
| protected: |
| NiceMock<content::MockDownloadManager>& manager() { return *manager_; } |
| download::MockDownloadItem& item(size_t index) { return *items_[index]; } |
| std::vector<std::unique_ptr<StrictMockDownloadItem>>& items() { |
| return items_; |
| } |
| NiceMock<MockDownloadDisplayController>& display_controller() { |
| return *display_controller_; |
| } |
| DownloadBubbleUIController& controller() { return *controller_; } |
| DownloadBubbleUIController& second_controller() { |
| return *second_controller_; |
| } |
| TestingProfile* profile() { return profile_; } |
| NiceMock<offline_items_collection::MockOfflineContentProvider>& |
| content_provider() { |
| return *content_provider_; |
| } |
| MockDownloadBubbleUpdateService* mock_update_service() { |
| return mock_update_service_.get(); |
| } |
| |
| void InitDownloadItem( |
| const base::FilePath::CharType* path, |
| DownloadState state, |
| const std::string& id, |
| bool is_transient = false, |
| base::Time start_time = base::Time::Now(), |
| bool may_show_animation = true, |
| download::DownloadItem::TargetDisposition target_disposition = |
| download::DownloadItem::TARGET_DISPOSITION_PROMPT, |
| const std::string& mime_type = "", |
| download::DownloadItem::DownloadCreationType creation_type = |
| download::DownloadItem::DownloadCreationType::TYPE_ACTIVE_DOWNLOAD) { |
| size_t index = items_.size(); |
| items_.push_back(std::make_unique<StrictMockDownloadItem>()); |
| EXPECT_CALL(item(index), GetId()) |
| .WillRepeatedly(Return(static_cast<uint32_t>(items_.size() + 1))); |
| EXPECT_CALL(item(index), GetGuid()).WillRepeatedly(ReturnRefOfCopy(id)); |
| EXPECT_CALL(item(index), GetState()).WillRepeatedly(Return(state)); |
| EXPECT_CALL(item(index), GetStartTime()).WillRepeatedly(Return(start_time)); |
| EXPECT_CALL(item(index), GetTargetFilePath()) |
| .WillRepeatedly( |
| ReturnRefOfCopy(base::FilePath(FILE_PATH_LITERAL("foo")))); |
| EXPECT_CALL(item(index), GetLastReason()) |
| .WillRepeatedly(Return(download::DOWNLOAD_INTERRUPT_REASON_NONE)); |
| EXPECT_CALL(item(index), GetInsecureDownloadStatus()) |
| .WillRepeatedly( |
| Return(download::DownloadItem::InsecureDownloadStatus::SAFE)); |
| int received_bytes = |
| state == download::DownloadItem::IN_PROGRESS ? 50 : 100; |
| EXPECT_CALL(item(index), GetReceivedBytes()) |
| .WillRepeatedly(Return(received_bytes)); |
| EXPECT_CALL(item(index), GetTotalBytes()).WillRepeatedly(Return(100)); |
| EXPECT_CALL(item(index), IsDone()).WillRepeatedly(Return(false)); |
| EXPECT_CALL(item(index), IsTransient()) |
| .WillRepeatedly(Return(is_transient)); |
| EXPECT_CALL(item(index), GetDownloadCreationType()) |
| .WillRepeatedly(Return(creation_type)); |
| EXPECT_CALL(item(index), IsPaused()).WillRepeatedly(Return(false)); |
| EXPECT_CALL(item(index), IsDangerous()).WillRepeatedly(Return(false)); |
| EXPECT_CALL(item(index), IsInsecure()).WillRepeatedly(Return(false)); |
| // Functions called when checking ShouldShowDownloadStartedAnimation(). |
| EXPECT_CALL(item(index), IsSavePackageDownload()) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(item(index), GetTargetDisposition()) |
| .WillRepeatedly(Return(target_disposition)); |
| EXPECT_CALL(item(index), GetMimeType()).WillRepeatedly(Return(mime_type)); |
| EXPECT_CALL(item(index), GetURL()) |
| .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); |
| EXPECT_CALL(item(index), GetReferrerUrl()) |
| .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); |
| EXPECT_CALL(item(index), GetDangerType()) |
| .WillRepeatedly( |
| Return(DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)); |
| std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> items; |
| for (size_t i = 0; i < items_.size(); ++i) { |
| items.push_back(&item(i)); |
| } |
| EXPECT_CALL(*manager_, GetAllDownloads(_)) |
| .WillRepeatedly(SetArgPointee<0>(items)); |
| EXPECT_CALL(*manager_, GetDownloadByGuid(id)) |
| .WillRepeatedly(Return(&(item(index)))); |
| content::DownloadItemUtils::AttachInfoForTesting(&(item(index)), profile(), |
| nullptr); |
| mock_update_service_->AddModel( |
| MockDownloadBubbleUpdateService::ModelType::kDownloadItem); |
| controller().OnDownloadItemAdded(&item(index), may_show_animation); |
| } |
| |
| void UpdateDownloadItem( |
| int item_index, |
| DownloadState state, |
| bool is_paused = false, |
| DownloadDangerType danger_type = |
| DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS) { |
| DCHECK_GT(items_.size(), static_cast<size_t>(item_index)); |
| EXPECT_CALL(item(item_index), GetState()).WillRepeatedly(Return(state)); |
| EXPECT_CALL(item(item_index), IsDone()) |
| .WillRepeatedly(Return(state == DownloadState::COMPLETE)); |
| EXPECT_CALL(item(item_index), IsDangerous()) |
| .WillRepeatedly( |
| Return(danger_type != |
| DownloadDangerType::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)); |
| EXPECT_CALL(item(item_index), GetDangerType()) |
| .WillRepeatedly(Return(danger_type)); |
| EXPECT_CALL(item(item_index), IsPaused()).WillRepeatedly(Return(is_paused)); |
| controller().OnDownloadItemUpdated(&item(item_index)); |
| } |
| |
| void InitOfflineItem(OfflineItemState state, std::string id) { |
| OfflineItem item; |
| item.state = state; |
| item.id.id = id; |
| offline_items_.push_back(item); |
| mock_update_service_->AddModel( |
| MockDownloadBubbleUpdateService::ModelType::kOfflineItem); |
| controller().OnOfflineItemsAdded({item}); |
| } |
| |
| void UpdateOfflineItem(int item_index, OfflineItemState state) { |
| offline_items_[item_index].state = state; |
| controller().OnOfflineItemUpdated(offline_items_[item_index]); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| std::unique_ptr<DownloadBubbleUIController> controller_; |
| std::unique_ptr<DownloadBubbleUIController> second_controller_; |
| std::unique_ptr<NiceMock<MockDownloadDisplayController>> display_controller_; |
| std::unique_ptr<NiceMock<MockDownloadDisplayController>> |
| second_display_controller_; |
| std::vector<std::unique_ptr<StrictMockDownloadItem>> items_; |
| OfflineItemList offline_items_; |
| raw_ptr<NiceMock<content::MockDownloadManager>, DanglingUntriaged> manager_; |
| TestingProfileManager testing_profile_manager_; |
| std::unique_ptr< |
| NiceMock<offline_items_collection::MockOfflineContentProvider>> |
| content_provider_; |
| std::unique_ptr<StrictMock<MockDownloadBubbleUpdateService>> |
| mock_update_service_; |
| std::unique_ptr<Browser> browser_; |
| raw_ptr<TestingProfile> profile_; |
| }; |
| |
| TEST_F(DownloadBubbleUIControllerTest, ProcessesNewItems) { |
| std::vector<std::string> ids = {"Download 1", "Download 2", "Offline 1", |
| "Download 3", "Offline 2"}; |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(2); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[0]); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), |
| download::DownloadItem::COMPLETE, ids[1]); |
| EXPECT_CALL(display_controller(), OnNewItem(false)).Times(1); |
| InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[2]); |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[3], |
| /*is_transient=*/false, base::Time::Now()); |
| EXPECT_CALL(display_controller(), OnNewItem(false)).Times(1); |
| InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[4]); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, ProcessesUpdatedItems) { |
| std::vector<std::string> ids = {"Download 1", "Offline 1"}; |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[0]); |
| EXPECT_CALL(display_controller(), OnUpdatedItem(false, true)).Times(1); |
| UpdateDownloadItem(/*item_index=*/0, DownloadState::IN_PROGRESS); |
| EXPECT_CALL(display_controller(), OnUpdatedItem(true, true)).Times(1); |
| UpdateDownloadItem(/*item_index=*/0, DownloadState::COMPLETE); |
| |
| EXPECT_CALL(display_controller(), OnNewItem(false)).Times(1); |
| InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]); |
| EXPECT_CALL(display_controller(), OnUpdatedItem(true, true)).Times(1); |
| UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, UpdatedItemIsPendingDeepScanning) { |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, "Download 1"); |
| EXPECT_CALL(display_controller(), OnUpdatedItem(true, true)).Times(1); |
| UpdateDownloadItem( |
| /*item_index=*/0, DownloadState::IN_PROGRESS, false, |
| DownloadDangerType::DOWNLOAD_DANGER_TYPE_PROMPT_FOR_SCANNING); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, TransientDownloadShouldNotShow) { |
| std::vector<std::string> ids = {"Download 1", "Download 2"}; |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[0], |
| /*is_transient=*/true); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[1], |
| /*is_transient=*/false); |
| std::vector<DownloadUIModelPtr> models = controller().GetMainView(); |
| EXPECT_EQ(models.size(), 1ul); |
| EXPECT_EQ(models[0]->GetContentId().id, ids[1]); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, |
| CompleteHistoryImportShouldNotShowInPartialView) { |
| std::vector<std::string> ids = {"history_import1", "history_import2"}; |
| // Complete history import item. |
| InitDownloadItem( |
| FILE_PATH_LITERAL("/foo/bar.pdf"), download::DownloadItem::COMPLETE, |
| ids[0], |
| /*is_transient=*/false, /*start_time=*/base::Time::Now(), |
| /*may_show_animation=*/true, |
| download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
| /*mime_type=*/"", |
| download::DownloadItem::DownloadCreationType::TYPE_HISTORY_IMPORT); |
| // In-progress history import item. |
| InitDownloadItem( |
| FILE_PATH_LITERAL("/foo/bar2.pdf"), download::DownloadItem::IN_PROGRESS, |
| ids[1], |
| /*is_transient=*/false, /*start_time=*/base::Time::Now(), |
| /*may_show_animation=*/true, |
| download::DownloadItem::TARGET_DISPOSITION_OVERWRITE, |
| /*mime_type=*/"", |
| download::DownloadItem::DownloadCreationType::TYPE_HISTORY_IMPORT); |
| std::vector<DownloadUIModelPtr> partial_view = controller().GetPartialView(); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| ASSERT_EQ(partial_view.size(), 1u); |
| EXPECT_EQ(partial_view[0]->GetContentId().id, ids[1]); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(partial_view.size(), 0u); |
| } |
| std::vector<DownloadUIModelPtr> main_view = controller().GetMainView(); |
| EXPECT_EQ(main_view.size(), 2u); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, |
| OpeningMainViewRemovesCompletedEntryFromPartialView) { |
| std::vector<std::string> ids = {"Download 1", "Offline 1"}; |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[0]); |
| InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]); |
| |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 2ul); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| EXPECT_EQ(second_controller().GetPartialView().size(), 2ul); |
| EXPECT_TRUE(second_controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0ul); |
| EXPECT_EQ(second_controller().GetPartialView().size(), 0ul); |
| } |
| |
| UpdateDownloadItem(/*item_index=*/0, DownloadState::COMPLETE); |
| UpdateOfflineItem(/*item_index=*/0, OfflineItemState::COMPLETE); |
| EXPECT_EQ(controller().GetMainView().size(), 2ul); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| // Download was removed from partial view because it is completed. |
| EXPECT_EQ(controller().GetPartialView().size(), 0ul); |
| // The partial view wasn't actually shown, so this bit is not updated. |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| EXPECT_EQ(second_controller().GetPartialView().size(), 0ul); |
| EXPECT_EQ(second_controller().last_primary_view_was_partial(), |
| download::IsDownloadBubblePartialViewEnabled(profile())); |
| } |
| |
| TEST_F(DownloadBubbleUIControllerTest, |
| OpeningMainViewDoesNotRemoveInProgressEntryFromPartialView) { |
| std::vector<std::string> ids = {"Download 1", "Offline 1"}; |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[0]); |
| InitOfflineItem(OfflineItemState::IN_PROGRESS, ids[1]); |
| |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 2ul); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0ul); |
| } |
| |
| // This does not remove the entries from the partial view because the items |
| // are in progress. |
| EXPECT_EQ(controller().GetMainView().size(), 2ul); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 2ul); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0ul); |
| } |
| } |
| |
| // Tests that no items are returned (i.e. no partial view will be shown) if it |
| // is too soon since the last partial view has been shown. |
| TEST_F(DownloadBubbleUIControllerTest, NoItemsReturnedForPartialViewTooSoon) { |
| std::vector<std::string> ids = {"Download 1", "Download 2", "Download 3", |
| "Download 4"}; |
| |
| // First time showing the partial view should work. |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar1.pdf"), |
| download::DownloadItem::COMPLETE, ids[0]); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 1u); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| } |
| |
| // No items are returned for a partial view because it is too soon. |
| task_environment_.FastForwardBy(base::Seconds(14)); |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), |
| download::DownloadItem::COMPLETE, ids[1]); |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| // The partial view wasn't actually shown, so this bit is not updated. |
| EXPECT_EQ(controller().last_primary_view_was_partial(), |
| download::IsDownloadBubblePartialViewEnabled(profile())); |
| |
| // Partial view can now be shown, and contains all the items. |
| task_environment_.FastForwardBy(base::Seconds(1)); |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar3.pdf"), |
| download::DownloadItem::COMPLETE, ids[1]); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 3u); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| } |
| |
| // Showing the main view even before time is up should still work. |
| task_environment_.FastForwardBy(base::Seconds(14)); |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| // The partial view wasn't actually shown, so this bit is not updated. |
| EXPECT_EQ(controller().last_primary_view_was_partial(), |
| download::IsDownloadBubblePartialViewEnabled(profile())); |
| EXPECT_EQ(controller().GetMainView().size(), 3u); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| |
| // Main view resets the partial view time, so the partial view can now be |
| // shown. |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar4.pdf"), |
| download::DownloadItem::IN_PROGRESS, ids[3]); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 1u); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| } |
| } |
| |
| // Tests that the partial view timer doesn't start if the partial view was |
| // empty and thus not shown. |
| TEST_F(DownloadBubbleUIControllerTest, EmptyPartialViewDoesNotPreventOpening) { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), |
| download::DownloadItem::COMPLETE, "Download"); |
| // Partial view is returned despite previous call to GetPartialView less than |
| // 15 seconds ago. |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 1u); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| } |
| } |
| |
| // Test that the preference suppresses the partial view. |
| TEST_F(DownloadBubbleUIControllerTest, PrefSuppressesPartialView) { |
| download::SetDownloadBubblePartialViewEnabled(profile(), false); |
| |
| EXPECT_CALL(display_controller(), OnNewItem(true)).Times(1); |
| InitDownloadItem(FILE_PATH_LITERAL("/foo/bar2.pdf"), |
| download::DownloadItem::COMPLETE, "Download"); |
| |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| EXPECT_FALSE(controller().last_primary_view_was_partial()); |
| |
| download::SetDownloadBubblePartialViewEnabled(profile(), true); |
| if (download::IsDownloadBubblePartialViewEnabled(profile())) { |
| EXPECT_EQ(controller().GetPartialView().size(), 1u); |
| EXPECT_TRUE(controller().last_primary_view_was_partial()); |
| } else { |
| EXPECT_EQ(controller().GetPartialView().size(), 0u); |
| } |
| } |
| |
| } // namespace |