OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.ntp.cards; | 5 package org.chromium.chrome.browser.ntp.cards; |
6 | 6 |
7 import static org.junit.Assert.assertEquals; | 7 import static org.junit.Assert.assertEquals; |
8 import static org.junit.Assert.assertFalse; | 8 import static org.junit.Assert.assertFalse; |
9 import static org.junit.Assert.assertNotEquals; | 9 import static org.junit.Assert.assertNotEquals; |
10 import static org.junit.Assert.assertThat; | |
11 import static org.junit.Assert.assertTrue; | 10 import static org.junit.Assert.assertTrue; |
12 import static org.junit.Assert.fail; | |
13 import static org.mockito.ArgumentMatchers.anyString; | 11 import static org.mockito.ArgumentMatchers.anyString; |
| 12 import static org.mockito.ArgumentMatchers.eq; |
14 import static org.mockito.Mockito.atLeastOnce; | 13 import static org.mockito.Mockito.atLeastOnce; |
15 import static org.mockito.Mockito.doNothing; | 14 import static org.mockito.Mockito.doNothing; |
| 15 import static org.mockito.Mockito.inOrder; |
16 import static org.mockito.Mockito.mock; | 16 import static org.mockito.Mockito.mock; |
17 import static org.mockito.Mockito.reset; | 17 import static org.mockito.Mockito.reset; |
18 import static org.mockito.Mockito.spy; | 18 import static org.mockito.Mockito.spy; |
19 import static org.mockito.Mockito.times; | 19 import static org.mockito.Mockito.times; |
20 import static org.mockito.Mockito.verify; | 20 import static org.mockito.Mockito.verify; |
| 21 import static org.mockito.Mockito.verifyNoMoreInteractions; |
21 import static org.mockito.Mockito.when; | 22 import static org.mockito.Mockito.when; |
22 | 23 |
23 import static org.chromium.base.test.util.Matchers.greaterThanOrEqualTo; | |
24 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsUnitTestUt
ils.bindViewHolders; | 24 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsUnitTestUt
ils.bindViewHolders; |
25 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsUnitTestUt
ils.makeUiConfig; | 25 import static org.chromium.chrome.browser.ntp.cards.ContentSuggestionsUnitTestUt
ils.makeUiConfig; |
26 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.createDummySuggestions; | 26 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.createDummySuggestions; |
27 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.explainFailedExpectation; | |
28 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.registerCategory; | 27 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.registerCategory; |
29 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.viewTypeToString; | 28 import static org.chromium.chrome.test.util.browser.suggestions.ContentSuggestio
nsTestUtils.stringify; |
30 | 29 |
31 import android.content.res.Resources; | 30 import android.content.res.Resources; |
32 import android.support.v7.widget.RecyclerView; | 31 import android.support.v7.widget.RecyclerView; |
33 import android.support.v7.widget.RecyclerView.AdapterDataObserver; | 32 import android.support.v7.widget.RecyclerView.AdapterDataObserver; |
34 import android.view.View; | 33 import android.view.View; |
35 | 34 |
36 import org.junit.After; | 35 import org.junit.After; |
37 import org.junit.Before; | 36 import org.junit.Before; |
38 import org.junit.Rule; | 37 import org.junit.Rule; |
39 import org.junit.Test; | 38 import org.junit.Test; |
40 import org.junit.runner.RunWith; | 39 import org.junit.runner.RunWith; |
41 import org.mockito.ArgumentCaptor; | 40 import org.mockito.ArgumentCaptor; |
| 41 import org.mockito.InOrder; |
42 import org.mockito.Mock; | 42 import org.mockito.Mock; |
43 import org.mockito.MockitoAnnotations; | 43 import org.mockito.MockitoAnnotations; |
| 44 import org.mockito.exceptions.base.MockitoAssertionError; |
| 45 import org.mockito.internal.verification.Times; |
| 46 import org.mockito.internal.verification.api.VerificationDataInOrder; |
| 47 import org.mockito.verification.VerificationMode; |
44 import org.robolectric.RuntimeEnvironment; | 48 import org.robolectric.RuntimeEnvironment; |
45 import org.robolectric.annotation.Config; | 49 import org.robolectric.annotation.Config; |
46 import org.robolectric.annotation.Implementation; | 50 import org.robolectric.annotation.Implementation; |
47 import org.robolectric.annotation.Implements; | 51 import org.robolectric.annotation.Implements; |
48 import org.robolectric.shadows.ShadowResources; | 52 import org.robolectric.shadows.ShadowResources; |
49 | 53 |
50 import org.chromium.base.Callback; | 54 import org.chromium.base.Callback; |
51 import org.chromium.base.ContextUtils; | 55 import org.chromium.base.ContextUtils; |
52 import org.chromium.base.test.util.Feature; | 56 import org.chromium.base.test.util.Feature; |
53 import org.chromium.chrome.R; | 57 import org.chromium.chrome.R; |
54 import org.chromium.chrome.browser.ChromeFeatureList; | 58 import org.chromium.chrome.browser.ChromeFeatureList; |
55 import org.chromium.chrome.browser.DisableHistogramsRule; | 59 import org.chromium.chrome.browser.DisableHistogramsRule; |
56 import org.chromium.chrome.browser.ntp.ContextMenuManager; | 60 import org.chromium.chrome.browser.ntp.ContextMenuManager; |
57 import org.chromium.chrome.browser.ntp.cards.SignInPromo.SigninObserver; | 61 import org.chromium.chrome.browser.ntp.cards.SignInPromo.SigninObserver; |
58 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; | 62 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; |
59 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; | 63 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; |
| 64 import org.chromium.chrome.browser.ntp.snippets.KnownCategories; |
60 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; | 65 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; |
61 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; | 66 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; |
62 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; | 67 import org.chromium.chrome.browser.preferences.ChromePreferenceManager; |
63 import org.chromium.chrome.browser.signin.SigninManager; | 68 import org.chromium.chrome.browser.signin.SigninManager; |
64 import org.chromium.chrome.browser.signin.SigninManager.SignInAllowedObserver; | 69 import org.chromium.chrome.browser.signin.SigninManager.SignInAllowedObserver; |
65 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; | 70 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; |
66 import org.chromium.chrome.browser.suggestions.ContentSuggestionsAdditionalActio
n; | 71 import org.chromium.chrome.browser.suggestions.ContentSuggestionsAdditionalActio
n; |
67 import org.chromium.chrome.browser.suggestions.DestructionObserver; | 72 import org.chromium.chrome.browser.suggestions.DestructionObserver; |
68 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter; | 73 import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter; |
69 import org.chromium.chrome.browser.suggestions.SuggestionsRanker; | 74 import org.chromium.chrome.browser.suggestions.SuggestionsRanker; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
101 private SigninManager mMockSigninManager; | 106 private SigninManager mMockSigninManager; |
102 @Mock | 107 @Mock |
103 private OfflinePageBridge mOfflinePageBridge; | 108 private OfflinePageBridge mOfflinePageBridge; |
104 @Mock | 109 @Mock |
105 private SuggestionsUiDelegate mUiDelegate; | 110 private SuggestionsUiDelegate mUiDelegate; |
106 | 111 |
107 /** | 112 /** |
108 * Stores information about a section that should be present in the adapter. | 113 * Stores information about a section that should be present in the adapter. |
109 */ | 114 */ |
110 private static class SectionDescriptor { | 115 private static class SectionDescriptor { |
111 public final int mNumSuggestions; | 116 public boolean mHeader = true; |
| 117 public final List<SnippetArticle> mSuggestions; |
112 public final boolean mStatusCard; | 118 public final boolean mStatusCard; |
113 public boolean mActionButton; | 119 public boolean mViewAllButton; |
| 120 public boolean mFetchButton; |
114 public boolean mProgressItem; | 121 public boolean mProgressItem; |
115 public SnippetArticle mFirstItem; | |
116 | 122 |
117 public SectionDescriptor(int numSuggestions) { | 123 public SectionDescriptor(List<SnippetArticle> suggestions) { |
118 mNumSuggestions = numSuggestions; | 124 mSuggestions = suggestions; |
119 mStatusCard = numSuggestions == 0; | 125 mStatusCard = suggestions.isEmpty(); |
120 } | 126 } |
121 | 127 |
122 public SectionDescriptor withActionButton() { | 128 public SectionDescriptor withoutHeader() { |
123 mActionButton = true; | 129 mHeader = false; |
| 130 return this; |
| 131 } |
| 132 |
| 133 public SectionDescriptor withViewAllButton() { |
| 134 mViewAllButton = true; |
| 135 return this; |
| 136 } |
| 137 |
| 138 public SectionDescriptor withFetchButton() { |
| 139 mFetchButton = true; |
124 return this; | 140 return this; |
125 } | 141 } |
126 | 142 |
127 public SectionDescriptor withProgress() { | 143 public SectionDescriptor withProgress() { |
128 mProgressItem = true; | 144 mProgressItem = true; |
129 return this; | 145 return this; |
130 } | 146 } |
131 | |
132 public SectionDescriptor withFirstItem(SnippetArticle firstItem) { | |
133 mFirstItem = firstItem; | |
134 return this; | |
135 } | |
136 } | 147 } |
137 | 148 |
138 /** | 149 /** |
139 * Checks the list of items from the adapter against a sequence of expectati
on, which is | 150 * Checks the list of items from the adapter against a sequence of expectati
on, which is |
140 * expressed as a sequence of calls to the {@link #expect} methods. | 151 * expressed as a sequence of calls to the {@code expect...()} methods. |
141 */ | 152 */ |
142 private static class ItemsMatcher { // TODO(pke): Find better name. | 153 private static class ItemsMatcher { // TODO(pke): Find better name. |
143 private final TreeNode mTreeNode; | 154 private final TreeNode mRoot; |
144 private int mCurrentIndex; | 155 private final NodeVisitor mVisitor = mock(NodeVisitor.class); |
| 156 private final InOrder mInOrder = inOrder(mVisitor); |
| 157 |
| 158 /** |
| 159 * The {@link org.mockito.internal.verification.Description} verificatio
n mode doesn't |
| 160 * support in-order verification, so we use a custom verification mode t
hat derives from the |
| 161 * default one. |
| 162 */ |
| 163 private final VerificationMode mVerification = new Times(1) { |
| 164 @Override |
| 165 public void verifyInOrder(VerificationDataInOrder data) { |
| 166 try { |
| 167 super.verifyInOrder(data); |
| 168 } catch (MockitoAssertionError e) { |
| 169 throw new MockitoAssertionError(e, stringify(mRoot)); |
| 170 } |
| 171 } |
| 172 }; |
145 | 173 |
146 public ItemsMatcher(TreeNode root) { | 174 public ItemsMatcher(TreeNode root) { |
147 mTreeNode = root; | 175 mRoot = root; |
| 176 root.visitItems(mVisitor); |
148 } | 177 } |
149 | 178 |
150 public void expect(@ItemViewType int expectedItemType) { | 179 public void expectSection(SectionDescriptor descriptor) { |
151 if (mCurrentIndex >= mTreeNode.getItemCount()) { | 180 if (descriptor.mHeader) { |
152 fail("Expected item of type " + viewTypeToString(expectedItemTyp
e) | 181 mInOrder.verify(mVisitor, mVerification).visitHeader(); |
153 + " but encountered end of list\n" | |
154 + explainFailedExpectation(mTreeNode, mCurrentIndex, exp
ectedItemType)); | |
155 } | |
156 if (mTreeNode.getItemViewType(mCurrentIndex) != expectedItemType) { | |
157 fail("Type mismatch at position " + mCurrentIndex + "\n" | |
158 + explainFailedExpectation(mTreeNode, mCurrentIndex, exp
ectedItemType)); | |
159 } | |
160 mCurrentIndex++; | |
161 } | |
162 | |
163 public void expect(SectionDescriptor descriptor) { | |
164 expect(ItemViewType.HEADER); | |
165 | |
166 if (descriptor.mFirstItem != null) { | |
167 if (mTreeNode.getSuggestionAt(mCurrentIndex) != descriptor.mFirs
tItem) { | |
168 fail("Wrong item at position " + mCurrentIndex + "\n" | |
169 + explainFailedExpectation( | |
170 mTreeNode, mCurrentIndex, ItemViewType.SNI
PPET)); | |
171 } | |
172 } | 182 } |
173 | 183 |
174 for (int i = 1; i <= descriptor.mNumSuggestions; i++) { | 184 for (SnippetArticle suggestion : descriptor.mSuggestions) { |
175 expect(ItemViewType.SNIPPET); | 185 mInOrder.verify(mVisitor, mVerification).visitSuggestion(eq(sugg
estion)); |
176 } | 186 } |
177 | 187 |
178 if (descriptor.mStatusCard) { | 188 if (descriptor.mStatusCard) { |
179 expect(ItemViewType.STATUS); | 189 mInOrder.verify(mVisitor, mVerification).visitNoSuggestionsItem(
); |
180 } | 190 } |
181 | 191 |
182 if (descriptor.mActionButton) { | 192 if (descriptor.mViewAllButton) { |
183 // TODO(bauerb): Verify the action. | 193 mInOrder.verify(mVisitor, mVerification) |
184 expect(ItemViewType.ACTION); | 194 .visitActionItem(ContentSuggestionsAdditionalAction.VIEW
_ALL); |
| 195 } |
| 196 |
| 197 if (descriptor.mFetchButton) { |
| 198 mInOrder.verify(mVisitor, mVerification) |
| 199 .visitActionItem(ContentSuggestionsAdditionalAction.FETC
H); |
185 } | 200 } |
186 | 201 |
187 if (descriptor.mProgressItem) { | 202 if (descriptor.mProgressItem) { |
188 expect(ItemViewType.PROGRESS); | 203 mInOrder.verify(mVisitor, mVerification).visitProgressItem(); |
189 } | 204 } |
190 } | 205 } |
191 | 206 |
| 207 public void expectAboveTheFoldItem() { |
| 208 mInOrder.verify(mVisitor, mVerification).visitAboveTheFoldItem(); |
| 209 } |
| 210 |
| 211 public void expectAllDismissedItem() { |
| 212 mInOrder.verify(mVisitor, mVerification).visitAllDismissedItem(); |
| 213 } |
| 214 |
| 215 public void expectFooter() { |
| 216 mInOrder.verify(mVisitor, mVerification).visitFooter(); |
| 217 } |
| 218 |
| 219 public void expectSpacingItem() { |
| 220 mInOrder.verify(mVisitor, mVerification).visitSpacingItem(); |
| 221 } |
| 222 |
192 public void expectEnd() { | 223 public void expectEnd() { |
193 assertEquals(mTreeNode.getItemCount(), mCurrentIndex); | 224 try { |
| 225 verifyNoMoreInteractions(mVisitor); |
| 226 } catch (MockitoAssertionError e) { |
| 227 throw new MockitoAssertionError(e, stringify(mRoot)); |
| 228 } |
194 } | 229 } |
195 } | 230 } |
196 | 231 |
197 @Before | 232 @Before |
198 public void setUp() { | 233 public void setUp() { |
199 MockitoAnnotations.initMocks(this); | 234 MockitoAnnotations.initMocks(this); |
200 | 235 |
201 ContextUtils.initApplicationContextForTests(RuntimeEnvironment.applicati
on); | 236 ContextUtils.initApplicationContextForTests(RuntimeEnvironment.applicati
on); |
202 | 237 |
203 // Set empty variation params for the test. | 238 // Set empty variation params for the test. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 @Test | 270 @Test |
236 @Feature({"Ntp"}) | 271 @Feature({"Ntp"}) |
237 public void testSuggestionLoading() { | 272 public void testSuggestionLoading() { |
238 assertItemsFor(sectionWithStatusCard().withProgress()); | 273 assertItemsFor(sectionWithStatusCard().withProgress()); |
239 | 274 |
240 final int numSuggestions = 3; | 275 final int numSuggestions = 3; |
241 List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions
, TEST_CATEGORY); | 276 List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions
, TEST_CATEGORY); |
242 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 277 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
243 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 278 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
244 | 279 |
245 assertItemsFor(section(numSuggestions)); | 280 assertItemsFor(section(suggestions)); |
246 } | 281 } |
247 | 282 |
248 /** | 283 /** |
249 * Tests that the adapter keeps listening for suggestion updates. | 284 * Tests that the adapter keeps listening for suggestion updates. |
250 */ | 285 */ |
251 @Test | 286 @Test |
252 @Feature({"Ntp"}) | 287 @Feature({"Ntp"}) |
253 public void testSuggestionLoadingInitiallyEmpty() { | 288 public void testSuggestionLoadingInitiallyEmpty() { |
254 // If we don't get anything, we should be in the same situation as the i
nitial one. | 289 // If we don't get anything, we should be in the same situation as the i
nitial one. |
255 mSource.setSuggestionsForCategory(TEST_CATEGORY, new ArrayList<SnippetAr
ticle>()); | 290 mSource.setSuggestionsForCategory(TEST_CATEGORY, new ArrayList<SnippetAr
ticle>()); |
256 assertItemsFor(sectionWithStatusCard().withProgress()); | 291 assertItemsFor(sectionWithStatusCard().withProgress()); |
257 | 292 |
258 // We should load new suggestions when we get notified about them. | 293 // We should load new suggestions when we get notified about them. |
259 final int numSuggestions = 5; | 294 final int numSuggestions = 5; |
260 | 295 |
261 List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions
, TEST_CATEGORY); | 296 List<SnippetArticle> suggestions = createDummySuggestions(numSuggestions
, TEST_CATEGORY); |
262 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 297 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
263 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 298 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
264 | 299 |
265 assertItemsFor(section(numSuggestions)); | 300 assertItemsFor(section(suggestions)); |
266 } | 301 } |
267 | 302 |
268 /** | 303 /** |
269 * Tests that the adapter clears the suggestions when asked to. | 304 * Tests that the adapter clears the suggestions when asked to. |
270 */ | 305 */ |
271 @Test | 306 @Test |
272 @Feature({"Ntp"}) | 307 @Feature({"Ntp"}) |
273 public void testSuggestionClearing() { | 308 public void testSuggestionClearing() { |
274 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); | 309 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); |
275 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 310 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
276 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 311 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
277 assertItemsFor(section(4)); | 312 assertItemsFor(section(suggestions)); |
278 | 313 |
279 // If we get told that the category is enabled, we just leave the curren
t suggestions do not | 314 // If we get told that the category is enabled, we just leave the curren
t suggestions do not |
280 // clear them. | 315 // clear them. |
281 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 316 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
282 assertItemsFor(section(4)); | 317 assertItemsFor(section(suggestions)); |
283 | 318 |
284 // When the category is disabled, the section should go away completely. | 319 // When the category is disabled, the section should go away completely. |
285 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); | 320 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); |
286 assertItemsFor(); | 321 assertItemsFor(); |
287 | 322 |
288 // Now we're in the "all dismissed" state. No suggestions should be acce
pted. | 323 // Now we're in the "all dismissed" state. No suggestions should be acce
pted. |
289 suggestions = createDummySuggestions(6, TEST_CATEGORY); | 324 suggestions = createDummySuggestions(6, TEST_CATEGORY); |
290 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 325 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
291 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 326 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
292 assertItemsFor(); | 327 assertItemsFor(); |
293 | 328 |
294 // After a full refresh, the adapter should accept suggestions again. | 329 // After a full refresh, the adapter should accept suggestions again. |
295 mSource.fireFullRefreshRequired(); | 330 mSource.fireFullRefreshRequired(); |
296 assertItemsFor(section(6)); | 331 assertItemsFor(section(suggestions)); |
297 } | 332 } |
298 | 333 |
299 /** | 334 /** |
300 * Tests that the adapter loads suggestions only when the status is favorabl
e. | 335 * Tests that the adapter loads suggestions only when the status is favorabl
e. |
301 */ | 336 */ |
302 @Test | 337 @Test |
303 @Feature({"Ntp"}) | 338 @Feature({"Ntp"}) |
304 public void testSuggestionLoadingBlock() { | 339 public void testSuggestionLoadingBlock() { |
305 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); | 340 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); |
306 | 341 |
307 // By default, status is INITIALIZING, so we can load suggestions. | 342 // By default, status is INITIALIZING, so we can load suggestions. |
308 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 343 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
309 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 344 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
310 assertItemsFor(section(3)); | 345 assertItemsFor(section(suggestions)); |
311 | 346 |
312 // Add another suggestion. | 347 // Add another suggestion. |
313 suggestions.add(new SnippetArticle(TEST_CATEGORY, "https://site.com/url1
", "title1", "pub1", | 348 suggestions.add(new SnippetArticle(TEST_CATEGORY, "https://site.com/url3
", "title3", "pub3", |
314 "txt1", "https://site.com/url1", 0, 0, 0)); | 349 "txt3", "https://site.com/url3", 0, 0, 0)); |
315 | 350 |
316 // When the provider is removed, we should not be able to load suggestio
ns. The UI should | 351 // When the provider is removed, we should not be able to load suggestio
ns. The UI should |
317 // stay the same though. | 352 // stay the same though. |
318 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED)
; | 353 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED)
; |
319 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 354 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
320 assertItemsFor(section(3)); | 355 assertItemsFor(section(suggestions.subList(0, 3))); |
321 | 356 |
322 // INITIALIZING lets us load suggestions still. | 357 // INITIALIZING lets us load suggestions still. |
323 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INITIALIZING)
; | 358 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INITIALIZING)
; |
324 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 359 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
325 assertItemsFor(sectionWithStatusCard().withProgress()); | 360 assertItemsFor(sectionWithStatusCard().withProgress()); |
326 | 361 |
327 // The adapter should now be waiting for new suggestions and the fourth
one should appear. | 362 // The adapter should now be waiting for new suggestions and the fourth
one should appear. |
328 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 363 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
329 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 364 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
330 assertItemsFor(section(4)); | 365 assertItemsFor(section(suggestions)); |
331 | 366 |
332 // When the category gets disabled, the section should go away and not l
oad any suggestions. | 367 // When the category gets disabled, the section should go away and not l
oad any suggestions. |
333 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); | 368 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); |
334 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 369 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
335 assertItemsFor(); | 370 assertItemsFor(); |
336 } | 371 } |
337 | 372 |
338 /** | 373 /** |
339 * Tests how the loading indicator reacts to status changes. | 374 * Tests how the loading indicator reacts to status changes. |
340 */ | 375 */ |
(...skipping 22 matching lines...) Expand all Loading... |
363 /** | 398 /** |
364 * Tests that the entire section disappears if its status switches to LOADIN
G_ERROR or | 399 * Tests that the entire section disappears if its status switches to LOADIN
G_ERROR or |
365 * CATEGORY_EXPLICITLY_DISABLED. Also tests that they are not shown when the
NTP reloads. | 400 * CATEGORY_EXPLICITLY_DISABLED. Also tests that they are not shown when the
NTP reloads. |
366 */ | 401 */ |
367 @Test | 402 @Test |
368 @Feature({"Ntp"}) | 403 @Feature({"Ntp"}) |
369 public void testSectionClearingWhenUnavailable() { | 404 public void testSectionClearingWhenUnavailable() { |
370 List<SnippetArticle> suggestions = createDummySuggestions(5, TEST_CATEGO
RY); | 405 List<SnippetArticle> suggestions = createDummySuggestions(5, TEST_CATEGO
RY); |
371 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 406 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
372 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 407 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
373 assertItemsFor(section(5)); | 408 assertItemsFor(section(suggestions)); |
374 | 409 |
375 // When the category goes away with a hard error, the section is cleared
from the UI. | 410 // When the category goes away with a hard error, the section is cleared
from the UI. |
376 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.LOADING_ERROR
); | 411 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.LOADING_ERROR
); |
377 assertItemsFor(); | 412 assertItemsFor(); |
378 | 413 |
379 // Same when loading a new NTP. | 414 // Same when loading a new NTP. |
380 reloadNtp(); | 415 reloadNtp(); |
381 assertItemsFor(); | 416 assertItemsFor(); |
382 | 417 |
383 // Same for CATEGORY_EXPLICITLY_DISABLED. | 418 // Same for CATEGORY_EXPLICITLY_DISABLED. |
384 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 419 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
385 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 420 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
386 reloadNtp(); | 421 reloadNtp(); |
387 assertItemsFor(section(5)); | 422 assertItemsFor(section(suggestions)); |
388 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); | 423 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.CATEGORY_EXPL
ICITLY_DISABLED); |
389 assertItemsFor(); | 424 assertItemsFor(); |
390 | 425 |
391 reloadNtp(); | 426 reloadNtp(); |
392 assertItemsFor(); | 427 assertItemsFor(); |
393 } | 428 } |
394 | 429 |
395 /** | 430 /** |
396 * Tests that the UI remains untouched if a category switches to NOT_PROVIDE
D. | 431 * Tests that the UI remains untouched if a category switches to NOT_PROVIDE
D. |
397 */ | 432 */ |
398 @Test | 433 @Test |
399 @Feature({"Ntp"}) | 434 @Feature({"Ntp"}) |
400 public void testUIUntouchedWhenNotProvided() { | 435 public void testUIUntouchedWhenNotProvided() { |
401 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); | 436 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); |
402 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 437 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
403 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 438 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
404 assertItemsFor(section(4)); | 439 assertItemsFor(section(suggestions)); |
405 | 440 |
406 // When the category switches to NOT_PROVIDED, UI stays the same. | 441 // When the category switches to NOT_PROVIDED, UI stays the same. |
407 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED)
; | 442 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED)
; |
408 mSource.silentlyRemoveCategory(TEST_CATEGORY); | 443 mSource.silentlyRemoveCategory(TEST_CATEGORY); |
409 assertItemsFor(section(4)); | 444 assertItemsFor(section(suggestions)); |
410 | 445 |
411 reloadNtp(); | 446 reloadNtp(); |
412 assertItemsFor(); | 447 assertItemsFor(); |
413 } | 448 } |
414 | 449 |
415 /** | 450 /** |
416 * Tests that the UI updates on updated suggestions. | 451 * Tests that the UI updates on updated suggestions. |
417 */ | 452 */ |
418 @Test | 453 @Test |
419 @Feature({"Ntp"}) | 454 @Feature({"Ntp"}) |
420 public void testUIUpdatesOnNewSuggestionsWhenOtherSectionSeen() { | 455 public void testUIUpdatesOnNewSuggestionsWhenOtherSectionSeen() { |
421 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); | 456 List<SnippetArticle> suggestions = createDummySuggestions(4, TEST_CATEGO
RY); |
422 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 457 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
423 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 458 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
424 | 459 |
425 @CategoryInt | 460 @CategoryInt |
426 final int otherCategory = TEST_CATEGORY + 1; | 461 final int otherCategory = TEST_CATEGORY + 1; |
427 List<SnippetArticle> otherSuggestions = createDummySuggestions(2, otherC
ategory); | 462 List<SnippetArticle> otherSuggestions = createDummySuggestions(2, otherC
ategory); |
428 mSource.setStatusForCategory(otherCategory, CategoryStatus.AVAILABLE); | 463 mSource.setStatusForCategory(otherCategory, CategoryStatus.AVAILABLE); |
429 mSource.setInfoForCategory( | 464 mSource.setInfoForCategory( |
430 otherCategory, new CategoryInfoBuilder(otherCategory).showIfEmpt
y().build()); | 465 otherCategory, new CategoryInfoBuilder(otherCategory).showIfEmpt
y().build()); |
431 mSource.setSuggestionsForCategory(otherCategory, otherSuggestions); | 466 mSource.setSuggestionsForCategory(otherCategory, otherSuggestions); |
432 | 467 |
433 reloadNtp(); | 468 reloadNtp(); |
434 assertItemsFor(section(4), section(2)); | 469 assertItemsFor(section(suggestions), section(otherSuggestions)); |
435 | 470 |
436 // Bind the whole section - indicate that it is being viewed. | 471 // Bind the whole section - indicate that it is being viewed. |
437 bindViewHolders(mAdapter.getSectionListForTesting().getSectionForTesting
(otherCategory)); | 472 bindViewHolders(mAdapter.getSectionListForTesting().getSectionForTesting
(otherCategory)); |
438 | 473 |
439 List<SnippetArticle> newSuggestions = createDummySuggestions(3, TEST_CAT
EGORY, "new"); | 474 List<SnippetArticle> newSuggestions = createDummySuggestions(3, TEST_CAT
EGORY, "new"); |
440 mSource.setSuggestionsForCategory(TEST_CATEGORY, newSuggestions); | 475 mSource.setSuggestionsForCategory(TEST_CATEGORY, newSuggestions); |
441 assertItemsFor(section(3), section(2)); | 476 assertItemsFor(section(newSuggestions), section(otherSuggestions)); |
442 | 477 |
443 reloadNtp(); | 478 reloadNtp(); |
444 assertItemsFor(section(3), section(2)); | 479 assertItemsFor(section(newSuggestions), section(otherSuggestions)); |
445 } | 480 } |
446 | 481 |
447 /** Tests whether a section stays visible if empty, if required. */ | 482 /** Tests whether a section stays visible if empty, if required. */ |
448 @Test | 483 @Test |
449 @Feature({"Ntp"}) | 484 @Feature({"Ntp"}) |
450 public void testSectionVisibleIfEmpty() { | 485 public void testSectionVisibleIfEmpty() { |
451 // Part 1: VisibleIfEmpty = true | 486 // Part 1: VisibleIfEmpty = true |
452 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); | 487 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); |
453 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); | 488 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); |
454 suggestionsSource.setInfoForCategory( | 489 suggestionsSource.setInfoForCategory( |
455 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).showIfEmpt
y().build()); | 490 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).showIfEmpt
y().build()); |
456 | 491 |
457 // 1.1 - Initial state | 492 // 1.1 - Initial state |
458 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); | 493 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
459 reloadNtp(); | 494 reloadNtp(); |
460 assertItemsFor(sectionWithStatusCard().withProgress()); | 495 assertItemsFor(sectionWithStatusCard().withProgress()); |
461 | 496 |
462 // 1.2 - With suggestions | 497 // 1.2 - With suggestions |
463 List<SnippetArticle> suggestions = | 498 List<SnippetArticle> suggestions = |
464 Collections.unmodifiableList(createDummySuggestions(3, TEST_CATE
GORY)); | 499 Collections.unmodifiableList(createDummySuggestions(3, TEST_CATE
GORY)); |
465 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); | 500 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); |
466 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 501 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
467 assertItemsFor(section(3)); | 502 assertItemsFor(section(suggestions)); |
468 | 503 |
469 // 1.3 - When all suggestions are dismissed | 504 // 1.3 - When all suggestions are dismissed |
470 SuggestionsSection section = | 505 SuggestionsSection section = |
471 mAdapter.getSectionListForTesting().getSectionForTesting(TEST_CA
TEGORY); | 506 mAdapter.getSectionListForTesting().getSectionForTesting(TEST_CA
TEGORY); |
472 assertSectionMatches(section(3), section); | 507 assertSectionMatches(section(suggestions), section); |
473 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); | 508 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); |
474 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); | 509 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); |
475 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); | 510 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); |
476 assertItemsFor(sectionWithStatusCard()); | 511 assertItemsFor(sectionWithStatusCard()); |
477 | 512 |
478 // Part 2: VisibleIfEmpty = false | 513 // Part 2: VisibleIfEmpty = false |
479 suggestionsSource = new FakeSuggestionsSource(); | 514 suggestionsSource = new FakeSuggestionsSource(); |
480 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); | 515 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); |
481 suggestionsSource.setInfoForCategory( | 516 suggestionsSource.setInfoForCategory( |
482 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).build()); | 517 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).build()); |
483 | 518 |
484 // 2.1 - Initial state | 519 // 2.1 - Initial state |
485 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); | 520 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
486 reloadNtp(); | 521 reloadNtp(); |
487 assertItemsFor(); | 522 assertItemsFor(); |
488 | 523 |
489 // 2.2 - With suggestions | 524 // 2.2 - With suggestions |
490 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); | 525 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); |
491 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 526 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
492 assertItemsFor(); | 527 assertItemsFor(); |
493 | 528 |
494 // 2.3 - When all suggestions are dismissed - N/A, suggestions don't get
added. | 529 // 2.3 - When all suggestions are dismissed - N/A, suggestions don't get
added. |
495 } | 530 } |
496 | 531 |
497 /** | 532 /** |
498 * Tests that the more button is shown for sections that declare it. | 533 * Tests that the more button is shown for sections that declare it. |
499 */ | 534 */ |
500 @Test | 535 @Test |
501 @Feature({"Ntp"}) | 536 @Feature({"Ntp"}) |
502 public void testMoreButton() { | 537 public void testViewAllButton() { |
503 // Part 1: With "View All" action | 538 // Part 1: With "View All" action |
504 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); | 539 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); |
505 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); | 540 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); |
506 suggestionsSource.setInfoForCategory(TEST_CATEGORY, | 541 suggestionsSource.setInfoForCategory(TEST_CATEGORY, |
507 new CategoryInfoBuilder(TEST_CATEGORY) | 542 new CategoryInfoBuilder(TEST_CATEGORY) |
508 .withAction(ContentSuggestionsAdditionalAction.VIEW_ALL) | 543 .withAction(ContentSuggestionsAdditionalAction.VIEW_ALL) |
509 .showIfEmpty() | 544 .showIfEmpty() |
510 .build()); | 545 .build()); |
511 | 546 |
512 // 1.1 - Initial state. | 547 // 1.1 - Initial state. |
513 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); | 548 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
514 reloadNtp(); | 549 reloadNtp(); |
515 assertItemsFor(sectionWithStatusCard().withActionButton().withProgress()
); | 550 assertItemsFor(sectionWithStatusCard().withViewAllButton().withProgress(
)); |
516 | 551 |
517 // 1.2 - With suggestions. | 552 // 1.2 - With suggestions. |
518 List<SnippetArticle> suggestions = | 553 List<SnippetArticle> suggestions = |
519 Collections.unmodifiableList(createDummySuggestions(3, TEST_CATE
GORY)); | 554 Collections.unmodifiableList(createDummySuggestions(3, TEST_CATE
GORY)); |
520 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); | 555 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); |
521 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 556 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
522 assertItemsFor(section(3).withActionButton()); | 557 assertItemsFor(section(suggestions).withViewAllButton()); |
523 | 558 |
524 // 1.3 - When all suggestions are dismissed. | 559 // 1.3 - When all suggestions are dismissed. |
525 SuggestionsSection section = | 560 SuggestionsSection section = |
526 mAdapter.getSectionListForTesting().getSectionForTesting(TEST_CA
TEGORY); | 561 mAdapter.getSectionListForTesting().getSectionForTesting(TEST_CA
TEGORY); |
527 assertSectionMatches(section(3).withActionButton(), section); | 562 assertSectionMatches(section(suggestions).withViewAllButton(), section); |
528 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); | 563 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); |
529 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); | 564 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); |
530 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); | 565 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); |
531 assertItemsFor(sectionWithStatusCard().withActionButton()); | 566 assertItemsFor(sectionWithStatusCard().withViewAllButton()); |
532 | 567 |
533 // Part 1: Without "View All" action | 568 // Part 1: Without "View All" action |
534 suggestionsSource = new FakeSuggestionsSource(); | 569 suggestionsSource = new FakeSuggestionsSource(); |
535 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); | 570 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.INI
TIALIZING); |
536 suggestionsSource.setInfoForCategory( | 571 suggestionsSource.setInfoForCategory( |
537 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).showIfEmpt
y().build()); | 572 TEST_CATEGORY, new CategoryInfoBuilder(TEST_CATEGORY).showIfEmpt
y().build()); |
538 | 573 |
539 // 2.1 - Initial state. | 574 // 2.1 - Initial state. |
540 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); | 575 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
541 reloadNtp(); | 576 reloadNtp(); |
542 assertItemsFor(sectionWithStatusCard().withProgress()); | 577 assertItemsFor(sectionWithStatusCard().withProgress()); |
543 | 578 |
544 // 2.2 - With suggestions. | 579 // 2.2 - With suggestions. |
545 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); | 580 suggestionsSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVA
ILABLE); |
546 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 581 suggestionsSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
547 assertItemsFor(section(3)); | 582 assertItemsFor(section(suggestions)); |
548 | 583 |
549 // 2.3 - When all suggestions are dismissed. | 584 // 2.3 - When all suggestions are dismissed. |
550 section = mAdapter.getSectionListForTesting().getSectionForTesting(TEST_
CATEGORY); | 585 section = mAdapter.getSectionListForTesting().getSectionForTesting(TEST_
CATEGORY); |
551 assertSectionMatches(section(3), section); | 586 assertSectionMatches(section(suggestions), section); |
552 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); | 587 section.removeSuggestionById(suggestions.get(0).mIdWithinCategory); |
553 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); | 588 section.removeSuggestionById(suggestions.get(1).mIdWithinCategory); |
554 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); | 589 section.removeSuggestionById(suggestions.get(2).mIdWithinCategory); |
555 assertItemsFor(sectionWithStatusCard()); | 590 assertItemsFor(sectionWithStatusCard()); |
556 } | 591 } |
557 | 592 |
558 /** | 593 /** |
| 594 * Tests that the more button is shown for sections that declare it. |
| 595 */ |
| 596 @Test |
| 597 @Feature({"Ntp"}) |
| 598 public void testFetchButton() { |
| 599 @CategoryInt |
| 600 final int category = TEST_CATEGORY; |
| 601 |
| 602 // Part 1: With "Fetch more" action |
| 603 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); |
| 604 suggestionsSource.setStatusForCategory(category, CategoryStatus.INITIALI
ZING); |
| 605 suggestionsSource.setInfoForCategory(category, |
| 606 new CategoryInfoBuilder(category) |
| 607 .withAction(ContentSuggestionsAdditionalAction.FETCH) |
| 608 .showIfEmpty() |
| 609 .build()); |
| 610 |
| 611 // 1.1 - Initial state. |
| 612 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
| 613 reloadNtp(); |
| 614 assertItemsFor(sectionWithStatusCard().withFetchButton().withProgress())
; |
| 615 |
| 616 // 1.2 - With suggestions. |
| 617 List<SnippetArticle> articles = |
| 618 Collections.unmodifiableList(createDummySuggestions(3, category)
); |
| 619 suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABL
E); |
| 620 suggestionsSource.setSuggestionsForCategory(category, articles); |
| 621 assertItemsFor(section(articles).withFetchButton()); |
| 622 |
| 623 // 1.3 - When all suggestions are dismissed. |
| 624 SuggestionsSection section = |
| 625 mAdapter.getSectionListForTesting().getSectionForTesting(categor
y); |
| 626 assertSectionMatches(section(articles).withFetchButton(), section); |
| 627 section.removeSuggestionById(articles.get(0).mIdWithinCategory); |
| 628 section.removeSuggestionById(articles.get(1).mIdWithinCategory); |
| 629 section.removeSuggestionById(articles.get(2).mIdWithinCategory); |
| 630 assertItemsFor(sectionWithStatusCard().withFetchButton()); |
| 631 |
| 632 // Part 1: Without "Fetch more" action |
| 633 suggestionsSource = new FakeSuggestionsSource(); |
| 634 suggestionsSource.setStatusForCategory(category, CategoryStatus.INITIALI
ZING); |
| 635 suggestionsSource.setInfoForCategory( |
| 636 category, new CategoryInfoBuilder(category).showIfEmpty().build(
)); |
| 637 |
| 638 // 2.1 - Initial state. |
| 639 when(mUiDelegate.getSuggestionsSource()).thenReturn(suggestionsSource); |
| 640 reloadNtp(); |
| 641 assertItemsFor(sectionWithStatusCard().withProgress()); |
| 642 |
| 643 // 2.2 - With suggestions. |
| 644 suggestionsSource.setStatusForCategory(category, CategoryStatus.AVAILABL
E); |
| 645 suggestionsSource.setSuggestionsForCategory(category, articles); |
| 646 assertItemsFor(section(articles)); |
| 647 |
| 648 // 2.3 - When all suggestions are dismissed. |
| 649 section = mAdapter.getSectionListForTesting().getSectionForTesting(categ
ory); |
| 650 assertSectionMatches(section(articles), section); |
| 651 section.removeSuggestionById(articles.get(0).mIdWithinCategory); |
| 652 section.removeSuggestionById(articles.get(1).mIdWithinCategory); |
| 653 section.removeSuggestionById(articles.get(2).mIdWithinCategory); |
| 654 assertItemsFor(sectionWithStatusCard()); |
| 655 } |
| 656 |
| 657 /** |
559 * Tests that invalidated suggestions are immediately removed. | 658 * Tests that invalidated suggestions are immediately removed. |
560 */ | 659 */ |
561 @Test | 660 @Test |
562 @Feature({"Ntp"}) | 661 @Feature({"Ntp"}) |
563 public void testSuggestionInvalidated() { | 662 public void testSuggestionInvalidated() { |
564 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); | 663 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); |
565 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 664 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
566 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 665 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
567 assertItemsFor(section(3)); | 666 assertItemsFor(section(suggestions)); |
568 assertArticlesEqual(suggestions, 2, 5); | |
569 | 667 |
570 SnippetArticle removed = suggestions.remove(1); | 668 SnippetArticle removed = suggestions.remove(1); |
571 mSource.fireSuggestionInvalidated(TEST_CATEGORY, removed.mIdWithinCatego
ry); | 669 mSource.fireSuggestionInvalidated(TEST_CATEGORY, removed.mIdWithinCatego
ry); |
572 assertArticlesEqual(suggestions, 2, 4); | 670 assertItemsFor(section(suggestions)); |
573 } | 671 } |
574 | 672 |
575 /** | 673 /** |
576 * Tests that the UI handles dynamically added (server-side) categories corr
ectly. | 674 * Tests that the UI handles dynamically added (server-side) categories corr
ectly. |
577 */ | 675 */ |
578 @Test | 676 @Test |
579 @Feature({"Ntp"}) | 677 @Feature({"Ntp"}) |
580 public void testDynamicCategories() { | 678 public void testDynamicCategories() { |
581 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); | 679 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); |
582 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); | 680 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
583 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); | 681 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
584 assertItemsFor(section(3)); | 682 assertItemsFor(section(suggestions)); |
585 | 683 |
586 int dynamicCategory1 = 1010; | 684 int dynamicCategory1 = 1010; |
587 List<SnippetArticle> dynamics1 = createDummySuggestions(5, dynamicCatego
ry1); | 685 List<SnippetArticle> dynamics1 = createDummySuggestions(5, dynamicCatego
ry1); |
588 mSource.setInfoForCategory(dynamicCategory1, | 686 mSource.setInfoForCategory(dynamicCategory1, |
589 new CategoryInfoBuilder(dynamicCategory1) | 687 new CategoryInfoBuilder(dynamicCategory1) |
590 .withAction(ContentSuggestionsAdditionalAction.VIEW_ALL) | 688 .withAction(ContentSuggestionsAdditionalAction.VIEW_ALL) |
591 .build()); | 689 .build()); |
592 mSource.setStatusForCategory(dynamicCategory1, CategoryStatus.AVAILABLE)
; | 690 mSource.setStatusForCategory(dynamicCategory1, CategoryStatus.AVAILABLE)
; |
593 mSource.setSuggestionsForCategory(dynamicCategory1, dynamics1); | 691 mSource.setSuggestionsForCategory(dynamicCategory1, dynamics1); |
594 reloadNtp(); | 692 reloadNtp(); |
595 | 693 |
596 assertItemsFor(section(3), section(5).withActionButton()); | 694 assertItemsFor(section(suggestions), section(dynamics1).withViewAllButto
n()); |
597 | 695 |
598 int dynamicCategory2 = 1011; | 696 int dynamicCategory2 = 1011; |
599 List<SnippetArticle> dynamics2 = createDummySuggestions(11, dynamicCateg
ory2); | 697 List<SnippetArticle> dynamics2 = createDummySuggestions(11, dynamicCateg
ory2); |
600 mSource.setInfoForCategory(dynamicCategory2, | 698 mSource.setInfoForCategory(dynamicCategory2, |
601 new CategoryInfoBuilder(dynamicCategory1).build()); | 699 new CategoryInfoBuilder(dynamicCategory1).build()); |
602 mSource.setStatusForCategory(dynamicCategory2, CategoryStatus.AVAILABLE)
; | 700 mSource.setStatusForCategory(dynamicCategory2, CategoryStatus.AVAILABLE)
; |
603 mSource.setSuggestionsForCategory(dynamicCategory2, dynamics2); | 701 mSource.setSuggestionsForCategory(dynamicCategory2, dynamics2); |
604 reloadNtp(); | 702 reloadNtp(); |
605 assertItemsFor(section(3), section(5).withActionButton(), section(11)); | 703 assertItemsFor( |
| 704 section(suggestions), section(dynamics1).withViewAllButton(), se
ction(dynamics2)); |
| 705 } |
| 706 |
| 707 @Test |
| 708 @Feature({"Ntp"}) |
| 709 public void testArticlesForYouSection() { |
| 710 // Show one section of suggestions from the test category, and one secti
on with Articles for |
| 711 // You. |
| 712 List<SnippetArticle> suggestions = createDummySuggestions(3, TEST_CATEGO
RY); |
| 713 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.AVAILABLE); |
| 714 mSource.setSuggestionsForCategory(TEST_CATEGORY, suggestions); |
| 715 |
| 716 mSource.setInfoForCategory(KnownCategories.ARTICLES, |
| 717 new CategoryInfoBuilder(KnownCategories.ARTICLES).build()); |
| 718 mSource.setStatusForCategory(KnownCategories.ARTICLES, CategoryStatus.AV
AILABLE); |
| 719 List<SnippetArticle> articles = createDummySuggestions(3, KnownCategorie
s.ARTICLES); |
| 720 mSource.setSuggestionsForCategory(KnownCategories.ARTICLES, articles); |
| 721 |
| 722 reloadNtp(); |
| 723 assertItemsFor(section(suggestions), section(articles)); |
| 724 |
| 725 // Remove the test category section. The remaining lone Articles for You
section should not |
| 726 // have a header. |
| 727 mSource.setStatusForCategory(TEST_CATEGORY, CategoryStatus.NOT_PROVIDED)
; |
| 728 reloadNtp(); |
| 729 assertItemsFor(section(articles).withoutHeader()); |
606 } | 730 } |
607 | 731 |
608 /** | 732 /** |
609 * Tests that the order of the categories is kept. | 733 * Tests that the order of the categories is kept. |
610 */ | 734 */ |
611 @Test | 735 @Test |
612 @Feature({"Ntp"}) | 736 @Feature({"Ntp"}) |
613 public void testCategoryOrder() { | 737 public void testCategoryOrder() { |
614 int[] categories = {TEST_CATEGORY, TEST_CATEGORY + 2, TEST_CATEGORY + 3,
TEST_CATEGORY + 4}; | 738 int[] categories = {TEST_CATEGORY, TEST_CATEGORY + 2, TEST_CATEGORY + 3,
TEST_CATEGORY + 4}; |
615 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); | 739 FakeSuggestionsSource suggestionsSource = new FakeSuggestionsSource(); |
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
955 } | 1079 } |
956 | 1080 |
957 /** | 1081 /** |
958 * Asserts that the given {@link TreeNode} is a {@link SuggestionsSection} t
hat matches the | 1082 * Asserts that the given {@link TreeNode} is a {@link SuggestionsSection} t
hat matches the |
959 * given {@link SectionDescriptor}. | 1083 * given {@link SectionDescriptor}. |
960 * @param descriptor The section descriptor to match against. | 1084 * @param descriptor The section descriptor to match against. |
961 * @param section The section from the adapter. | 1085 * @param section The section from the adapter. |
962 */ | 1086 */ |
963 private void assertSectionMatches(SectionDescriptor descriptor, SuggestionsS
ection section) { | 1087 private void assertSectionMatches(SectionDescriptor descriptor, SuggestionsS
ection section) { |
964 ItemsMatcher matcher = new ItemsMatcher(section); | 1088 ItemsMatcher matcher = new ItemsMatcher(section); |
965 matcher.expect(descriptor); | 1089 matcher.expectSection(descriptor); |
966 matcher.expectEnd(); | 1090 matcher.expectEnd(); |
967 } | 1091 } |
968 | 1092 |
969 /** | 1093 /** |
970 * Asserts that {@link #mAdapter}.{@link NewTabPageAdapter#getItemCount()} c
orresponds to an NTP | 1094 * Asserts that {@link #mAdapter}.{@link NewTabPageAdapter#getItemCount()} c
orresponds to an NTP |
971 * with the given sections in it. | 1095 * with the given sections in it. |
972 * | 1096 * |
973 * @param descriptors A list of descriptors, each describing a section that
should be present on | 1097 * @param descriptors A list of descriptors, each describing a section that
should be present on |
974 * the UI. | 1098 * the UI. |
975 */ | 1099 */ |
976 private void assertItemsFor(SectionDescriptor... descriptors) { | 1100 private void assertItemsFor(SectionDescriptor... descriptors) { |
977 ItemsMatcher matcher = new ItemsMatcher(mAdapter.getRootForTesting()); | 1101 ItemsMatcher matcher = new ItemsMatcher(mAdapter.getRootForTesting()); |
978 matcher.expect(ItemViewType.ABOVE_THE_FOLD); | 1102 matcher.expectAboveTheFoldItem(); |
979 for (SectionDescriptor descriptor : descriptors) matcher.expect(descript
or); | 1103 for (SectionDescriptor descriptor : descriptors) matcher.expectSection(d
escriptor); |
980 if (descriptors.length == 0) { | 1104 if (descriptors.length == 0) { |
981 matcher.expect(ItemViewType.ALL_DISMISSED); | 1105 matcher.expectAllDismissedItem(); |
982 } else { | 1106 } else { |
983 matcher.expect(ItemViewType.FOOTER); | 1107 matcher.expectFooter(); |
984 } | 1108 } |
985 matcher.expect(ItemViewType.SPACING); | 1109 matcher.expectSpacingItem(); |
986 matcher.expectEnd(); | 1110 matcher.expectEnd(); |
987 } | 1111 } |
988 | 1112 |
989 /** | 1113 /** |
990 * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a sect
ion with | 1114 * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a sect
ion with |
991 * {@code numSuggestions} cards in it. | 1115 * {@code numSuggestions} cards in it. |
992 * @param numSuggestions The number of suggestions in the section. If there
are zero, use either | 1116 * @param suggestions The list of suggestions in the section. If the list is
empty, use either |
993 * no section at all (if it is not displayed) or | 1117 * no section at all (if it is not displayed) or |
994 * {@link #sectionWithStatusCard()}. | 1118 * {@link #sectionWithStatusCard()}. |
995 * @return A descriptor for the section. | 1119 * @return A descriptor for the section. |
996 */ | 1120 */ |
997 private SectionDescriptor section(int numSuggestions) { | 1121 private SectionDescriptor section(List<SnippetArticle> suggestions) { |
998 assert numSuggestions > 0; | 1122 assert !suggestions.isEmpty(); |
999 return new SectionDescriptor(numSuggestions); | 1123 return new SectionDescriptor(suggestions); |
1000 } | 1124 } |
1001 | 1125 |
1002 /** | 1126 /** |
1003 * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a sect
ion that has no | 1127 * To be used with {@link #assertItemsFor(SectionDescriptor...)}, for a sect
ion that has no |
1004 * suggestions, but a status card to be displayed. | 1128 * suggestions, but a status card to be displayed. |
1005 * @return A descriptor for the section. | 1129 * @return A descriptor for the section. |
1006 */ | 1130 */ |
1007 private SectionDescriptor sectionWithStatusCard() { | 1131 private SectionDescriptor sectionWithStatusCard() { |
1008 return new SectionDescriptor(0); | 1132 return new SectionDescriptor(Collections.<SnippetArticle>emptyList()); |
1009 } | 1133 } |
1010 | 1134 |
1011 private void reloadNtp() { | 1135 private void reloadNtp() { |
1012 mAdapter = new NewTabPageAdapter(mUiDelegate, mock(View.class), makeUiCo
nfig(), | 1136 mAdapter = new NewTabPageAdapter(mUiDelegate, mock(View.class), makeUiCo
nfig(), |
1013 mOfflinePageBridge, mock(ContextMenuManager.class), /* tileGroup
Delegate = | 1137 mOfflinePageBridge, mock(ContextMenuManager.class), /* tileGroup
Delegate = |
1014 */ null); | 1138 */ null); |
1015 mAdapter.refreshSuggestions(); | 1139 mAdapter.refreshSuggestions(); |
1016 } | 1140 } |
1017 | 1141 |
1018 private void assertArticlesEqual(List<SnippetArticle> articles, int start, i
nt end) { | |
1019 assertThat(mAdapter.getItemCount(), greaterThanOrEqualTo(end)); | |
1020 for (int i = start; i < end; i++) { | |
1021 assertEquals(articles.get(i - start), mAdapter.getSuggestionAt(i)); | |
1022 } | |
1023 } | |
1024 | |
1025 private boolean isSignInPromoVisible() { | 1142 private boolean isSignInPromoVisible() { |
1026 return mAdapter.getFirstPositionForType(ItemViewType.PROMO) != RecyclerV
iew.NO_POSITION; | 1143 return mAdapter.getFirstPositionForType(ItemViewType.PROMO) != RecyclerV
iew.NO_POSITION; |
1027 } | 1144 } |
1028 | 1145 |
1029 private int getCategory(TreeNode item) { | 1146 private int getCategory(TreeNode item) { |
1030 return ((SuggestionsSection) item).getCategory(); | 1147 return ((SuggestionsSection) item).getCategory(); |
1031 } | 1148 } |
1032 } | 1149 } |
OLD | NEW |