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