Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Side by Side Diff: chrome/android/junit/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapterTest.java

Issue 2860463002: [Suggestions] Remove TreeNode.getSuggestionAt() in favor of a visitor. (Closed)
Patch Set: review Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698