1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/ntp_snippets/reading_list/reading_list_suggestions_provider.h"
6 
7 #include <memory>
8 
9 #include "base/test/simple_test_clock.h"
10 #include "components/ntp_snippets/mock_content_suggestions_provider_observer.h"
11 #include "components/reading_list/core/reading_list_model_impl.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace ntp_snippets {
16 
17 namespace {
18 
19 const char kTitleUnread1[] = "title1";
20 const char kTitleUnread2[] = "title2";
21 const char kTitleUnread3[] = "title3";
22 const char kTitleUnread4[] = "title4";
23 const char kTitleRead1[] = "title_read1";
24 
25 using ::testing::_;
26 using ::testing::ElementsAre;
27 using ::testing::IsEmpty;
28 using ::testing::Property;
29 
30 class ReadingListSuggestionsProviderTest : public ::testing::Test {
31  public:
ReadingListSuggestionsProviderTest()32   ReadingListSuggestionsProviderTest() {
33     model_ = std::make_unique<ReadingListModelImpl>(
34         /*storage_layer=*/nullptr, /*pref_service=*/nullptr, &clock_);
35   }
36 
CreateProvider()37   void CreateProvider() {
38     EXPECT_CALL(observer_,
39                 OnCategoryStatusChanged(_, ReadingListCategory(),
40                                         CategoryStatus::AVAILABLE_LOADING))
41         .RetiresOnSaturation();
42     EXPECT_CALL(observer_, OnCategoryStatusChanged(_, ReadingListCategory(),
43                                                    CategoryStatus::AVAILABLE))
44         .RetiresOnSaturation();
45     provider_ = std::make_unique<ReadingListSuggestionsProvider>(&observer_,
46                                                                  model_.get());
47   }
48 
ReadingListCategory()49   Category ReadingListCategory() {
50     return Category::FromKnownCategory(KnownCategories::READING_LIST);
51   }
52 
AddEntries()53   void AddEntries() {
54     model_->AddEntry(url_unread1_, kTitleUnread1,
55                      reading_list::ADDED_VIA_CURRENT_APP);
56     clock_.Advance(base::TimeDelta::FromMilliseconds(10));
57     model_->AddEntry(url_unread2_, kTitleUnread2,
58                      reading_list::ADDED_VIA_CURRENT_APP);
59     clock_.Advance(base::TimeDelta::FromMilliseconds(10));
60     model_->AddEntry(url_read1_, kTitleRead1,
61                      reading_list::ADDED_VIA_CURRENT_APP);
62     model_->SetReadStatus(url_read1_, true);
63     clock_.Advance(base::TimeDelta::FromMilliseconds(10));
64     model_->AddEntry(url_unread3_, kTitleUnread3,
65                      reading_list::ADDED_VIA_CURRENT_APP);
66     clock_.Advance(base::TimeDelta::FromMilliseconds(10));
67     model_->AddEntry(url_unread4_, kTitleUnread4,
68                      reading_list::ADDED_VIA_CURRENT_APP);
69   }
70 
71  protected:
72   base::SimpleTestClock clock_;
73   std::unique_ptr<ReadingListModelImpl> model_;
74   testing::StrictMock<MockContentSuggestionsProviderObserver> observer_;
75   std::unique_ptr<ReadingListSuggestionsProvider> provider_;
76 
77   const GURL url_unread1_{"http://www.foo1.bar"};
78   const GURL url_unread2_{"http://www.foo2.bar"};
79   const GURL url_unread3_{"http://www.foo3.bar"};
80   const GURL url_unread4_{"http://www.foo4.bar"};
81   const GURL url_read1_{"http://www.bar.foor"};
82 };
83 
TEST_F(ReadingListSuggestionsProviderTest,CategoryInfo)84 TEST_F(ReadingListSuggestionsProviderTest, CategoryInfo) {
85   EXPECT_CALL(observer_, OnNewSuggestions(_, ReadingListCategory(), IsEmpty()))
86       .RetiresOnSaturation();
87   CreateProvider();
88 
89   CategoryInfo categoryInfo = provider_->GetCategoryInfo(ReadingListCategory());
90   EXPECT_EQ(ContentSuggestionsAdditionalAction::VIEW_ALL,
91             categoryInfo.additional_action());
92 }
93 
TEST_F(ReadingListSuggestionsProviderTest,ReturnsThreeLatestUnreadSuggestion)94 TEST_F(ReadingListSuggestionsProviderTest, ReturnsThreeLatestUnreadSuggestion) {
95   AddEntries();
96 
97   EXPECT_CALL(
98       observer_,
99       OnNewSuggestions(
100           _, ReadingListCategory(),
101           ElementsAre(Property(&ContentSuggestion::url, url_unread4_),
102                       Property(&ContentSuggestion::url, url_unread3_),
103                       Property(&ContentSuggestion::url, url_unread2_))));
104 
105   CreateProvider();
106 }
107 
108 // Tests that the provider returns only unread suggestions even if there is less
109 // unread suggestions than the maximum number of suggestions.
TEST_F(ReadingListSuggestionsProviderTest,ReturnsOnlyUnreadSuggestion)110 TEST_F(ReadingListSuggestionsProviderTest, ReturnsOnlyUnreadSuggestion) {
111   GURL url_unread1 = GURL("http://www.foo1.bar");
112   GURL url_read1 = GURL("http://www.bar.foor");
113   std::string title_unread1 = "title1";
114   std::string title_read1 = "title_read1";
115   model_->AddEntry(url_unread1, title_unread1,
116                    reading_list::ADDED_VIA_CURRENT_APP);
117   clock_.Advance(base::TimeDelta::FromMilliseconds(10));
118   model_->AddEntry(url_read1, title_read1, reading_list::ADDED_VIA_CURRENT_APP);
119   model_->SetReadStatus(url_read1, true);
120 
121   EXPECT_CALL(observer_,
122               OnNewSuggestions(
123                   _, ReadingListCategory(),
124                   ElementsAre(Property(&ContentSuggestion::url, url_unread1))));
125 
126   CreateProvider();
127 }
128 
TEST_F(ReadingListSuggestionsProviderTest,DismissesEntry)129 TEST_F(ReadingListSuggestionsProviderTest, DismissesEntry) {
130   AddEntries();
131 
132   EXPECT_CALL(
133       observer_,
134       OnNewSuggestions(
135           _, ReadingListCategory(),
136           ElementsAre(Property(&ContentSuggestion::url, url_unread4_),
137                       Property(&ContentSuggestion::url, url_unread3_),
138                       Property(&ContentSuggestion::url, url_unread2_))));
139 
140   CreateProvider();
141 
142   EXPECT_CALL(
143       observer_,
144       OnNewSuggestions(
145           _, ReadingListCategory(),
146           ElementsAre(Property(&ContentSuggestion::url, url_unread4_),
147                       Property(&ContentSuggestion::url, url_unread2_),
148                       Property(&ContentSuggestion::url, url_unread1_))));
149 
150   provider_->DismissSuggestion(
151       ContentSuggestion::ID(ReadingListCategory(), url_unread3_.spec()));
152 }
153 
154 }  // namespace
155 
156 }  // namespace ntp_snippets
157