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