blob: 125cbdf86c94f7a12d80be81dd2114118e00c2e5 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.download;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.longClick;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.not;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.StringRes;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils;
import android.view.View;
import android.widget.Spinner;
import android.widget.TextView;
import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.ContextUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.FlakyTest;
import org.chromium.base.test.util.Restriction;
import org.chromium.base.test.util.RetryOnFailure;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.download.ui.DownloadHistoryAdapter;
import org.chromium.chrome.browser.download.ui.DownloadHistoryItemViewHolder;
import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper;
import org.chromium.chrome.browser.download.ui.DownloadHistoryItemWrapper.OfflineItemWrapper;
import org.chromium.chrome.browser.download.ui.DownloadItemView;
import org.chromium.chrome.browser.download.ui.DownloadManagerToolbar;
import org.chromium.chrome.browser.download.ui.DownloadManagerUi;
import org.chromium.chrome.browser.download.ui.SpaceDisplay;
import org.chromium.chrome.browser.download.ui.StubbedProvider;
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.chrome.browser.widget.ListMenuButton.Item;
import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
import org.chromium.components.offline_items_collection.OfflineItem;
import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.ui.test.util.UiRestriction;
import java.util.HashMap;
import java.util.List;
/**
* Tests the DownloadActivity and the DownloadManagerUi.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
public class DownloadActivityTest {
// Disable animations to reduce flakiness.
@ClassRule
public static DisableAnimationsTestRule disableAnimationsRule = new DisableAnimationsTestRule();
@Rule
public ActivityTestRule<DownloadActivity> mActivityTestRule =
new ActivityTestRule<>(DownloadActivity.class);
private static class TestObserver extends RecyclerView.AdapterDataObserver
implements SelectionObserver<DownloadHistoryItemWrapper>, SpaceDisplay.Observer {
public final CallbackHelper onChangedCallback = new CallbackHelper();
public final CallbackHelper onSelectionCallback = new CallbackHelper();
public final CallbackHelper onSpaceDisplayUpdatedCallback = new CallbackHelper();
private List<DownloadHistoryItemWrapper> mOnSelectionItems;
private Handler mHandler;
public TestObserver() {
mHandler = new Handler(Looper.getMainLooper());
}
@Override
public void onChanged() {
// To guarantee that all real Observers have had a chance to react to the event, post
// the CallbackHelper.notifyCalled() call.
mHandler.post(() -> onChangedCallback.notifyCalled());
}
@Override
public void onSelectionStateChange(List<DownloadHistoryItemWrapper> selectedItems) {
mOnSelectionItems = selectedItems;
mHandler.post(() -> onSelectionCallback.notifyCalled());
}
@Override
public void onSpaceDisplayUpdated(SpaceDisplay display) {
mHandler.post(() -> onSpaceDisplayUpdatedCallback.notifyCalled());
}
}
private static final String PREF_SHOW_STORAGE_INFO_HEADER =
"download_home_show_storage_info_header";
private StubbedProvider mStubbedProvider;
private TestObserver mAdapterObserver;
private DownloadManagerUi mUi;
private DownloadHistoryAdapter mAdapter;
private RecyclerView mRecyclerView;
private TextView mSpaceUsedDisplay;
@Before
public void setUp() throws Exception {
Editor editor = ContextUtils.getAppSharedPreferences().edit();
editor.putBoolean(PREF_SHOW_STORAGE_INFO_HEADER, true).apply();
ThreadUtils.runOnUiThreadBlocking(() -> {
PrefServiceBridge.getInstance().setPromptForDownloadAndroid(
DownloadPromptStatus.DONT_SHOW);
});
HashMap<String, Boolean> features = new HashMap<String, Boolean>();
features.put(ChromeFeatureList.DOWNLOADS_LOCATION_CHANGE, false);
features.put(ChromeFeatureList.DOWNLOAD_HOME_SHOW_STORAGE_INFO, false);
features.put(ChromeFeatureList.DOWNLOAD_HOME_V2, false);
features.put(ChromeFeatureList.OVERSCROLL_HISTORY_NAVIGATION, false);
ChromeFeatureList.setTestFeatures(features);
mStubbedProvider = new StubbedProvider();
DownloadManagerUi.setProviderForTests(mStubbedProvider);
mAdapterObserver = new TestObserver();
mStubbedProvider.getSelectionDelegate().addObserver(mAdapterObserver);
startDownloadActivity();
mUi = mActivityTestRule.getActivity().getDownloadManagerUiForTests();
mStubbedProvider.setUIDelegate(mUi);
mAdapter = mUi.getDownloadHistoryAdapterForTests();
mAdapter.registerAdapterDataObserver(mAdapterObserver);
mSpaceUsedDisplay = (TextView) mActivityTestRule.getActivity().findViewById(
org.chromium.chrome.download.R.id.size_downloaded);
mRecyclerView =
((RecyclerView) mActivityTestRule.getActivity().findViewById(R.id.recycler_view));
mAdapter.getSpaceDisplayForTests().addObserverForTests(mAdapterObserver);
}
@Test
@MediumTest
public void testSpaceDisplay() throws Exception {
// This first check is a Criteria because initialization of the Adapter is asynchronous.
onView(withText("6.00 GB downloaded")).check(matches(isDisplayed()));
// Add a new item.
int callCount = mAdapterObserver.onChangedCallback.getCallCount();
int spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final DownloadItem updateItem = StubbedProvider.createDownloadItem(7, "20151021 07:28");
ThreadUtils.runOnUiThread(() -> mAdapter.onDownloadItemCreated(updateItem));
mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
// Use Criteria here because the text for SpaceDisplay is updated through an AsyncTask.
onView(withText("6.50 GB downloaded")).check(matches(isDisplayed()));
// Mark one download as deleted on disk, which should prevent it from being counted.
callCount = mAdapterObserver.onChangedCallback.getCallCount();
spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final DownloadItem deletedItem = StubbedProvider.createDownloadItem(6, "20151021 07:28");
deletedItem.setHasBeenExternallyRemoved(true);
ThreadUtils.runOnUiThread(() -> mAdapter.onDownloadItemUpdated(deletedItem));
mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
onView(withText("5.50 GB downloaded")).check(matches(isDisplayed()));
// Say that the offline page has been deleted.
callCount = mAdapterObserver.onChangedCallback.getCallCount();
spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final OfflineItem deletedPage = StubbedProvider.createOfflineItem(3, "20151021 07:28");
ThreadUtils.runOnUiThread(
()
-> mStubbedProvider.getOfflineContentProvider().observer.onItemRemoved(
deletedPage.id));
mAdapterObserver.onChangedCallback.waitForCallback(callCount, 2);
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
onView(withText("512.00 MB downloaded")).check(matches(isDisplayed()));
}
/**
* Clicking on filters affects various things in the UI.
*/
@DisabledTest(message = "crbug.com/855389")
@Test
@MediumTest
public void testFilters() throws Exception {
// This first check is a Criteria because initialization of the Adapter is asynchronous.
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
}
});
// Change the filter to Pages. Only the space display, offline page and the date header
// should stay.
int spaceDisplayCallCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
clickOnFilter(mUi, 1);
Assert.assertEquals(3, mAdapter.getItemCount());
// Check that the number of items displayed is correct.
// We need to poll because RecyclerView doesn't animate changes immediately.
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return mRecyclerView.getChildCount() == 3;
}
});
// Filtering doesn't affect the total download size.
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(spaceDisplayCallCount);
Assert.assertEquals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
}
@Test
@MediumTest
@RetryOnFailure
@FlakyTest(message = "crbug.com/854241")
public void testDeleteFiles() throws Exception {
SnackbarManager.setDurationForTesting(1);
// This first check is a Criteria because initialization of the Adapter is asynchronous.
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
}
});
// Select the first two items.
toggleItemSelection(2);
toggleItemSelection(3);
// Click the delete button, which should delete the items and reset the toolbar.
Assert.assertEquals(12, mAdapter.getItemCount());
// checkForExternallyRemovedFiles() should have been called once already in onResume().
Assert.assertEquals(
1, mStubbedProvider.getDownloadDelegate().checkExternalCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getDownloadDelegate().removeDownloadCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getOfflineContentProvider().deleteItemCallback.getCallCount());
int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
ThreadUtils.runOnUiThread(
()
-> Assert.assertTrue(
mUi.getDownloadManagerToolbarForTests()
.getMenu()
.performIdentifierAction(
R.id.selection_mode_delete_menu_id, 0)));
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
Assert.assertEquals(
1, mStubbedProvider.getDownloadDelegate().checkExternalCallback.getCallCount());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
Assert.assertEquals(9, mAdapter.getItemCount());
Assert.assertEquals("0.65 KB downloaded", mSpaceUsedDisplay.getText());
}
@Test
@MediumTest
@RetryOnFailure
@FlakyTest(message = "crbug.com/855219")
public void testDeleteFileFromMenu() throws Exception {
SnackbarManager.setDurationForTesting(1);
// This first check is a Criteria because initialization of the Adapter is asynchronous.
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
}
});
Assert.assertEquals(12, mAdapter.getItemCount());
// checkForExternallyRemovedFiles() should have been called once already in onResume().
Assert.assertEquals(
1, mStubbedProvider.getDownloadDelegate().checkExternalCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getDownloadDelegate().removeDownloadCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getOfflineContentProvider().deleteItemCallback.getCallCount());
int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
// Simulate a delete context menu action on the item.
simulateContextMenu(2, R.string.delete);
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
Assert.assertEquals(
1, mStubbedProvider.getDownloadDelegate().checkExternalCallback.getCallCount());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
Assert.assertEquals(11, mAdapter.getItemCount());
Assert.assertEquals("5.00 GB downloaded", mSpaceUsedDisplay.getText());
}
@DisabledTest(message = "crbug.com/855389")
@Test
@MediumTest
@RetryOnFailure
public void testUndoDelete() throws Exception {
// Adapter positions:
// 0 = space display
// 1 = date
// 2 = download item #7
// 3 = download item #8
// 4 = date
// 5 = download item #6
// 6 = offline page #3
SnackbarManager.setDurationForTesting(5000);
// Add duplicate items.
int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final DownloadItem item7 = StubbedProvider.createDownloadItem(7, "20161021 07:28");
final DownloadItem item8 = StubbedProvider.createDownloadItem(8, "20161021 17:28");
ThreadUtils.runOnUiThread(() -> {
mAdapter.onDownloadItemCreated(item7);
mAdapter.onDownloadItemCreated(item8);
});
// The criteria is needed because an AsyncTask is fired to update the space display, which
// can result in either 1 or 2 updates.
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
});
// Select download item #7 and offline page #3.
toggleItemSelection(2);
toggleItemSelection(6);
Assert.assertEquals(15, mAdapter.getItemCount());
// Click the delete button.
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
ThreadUtils.runOnUiThread(
()
-> Assert.assertTrue(
mUi.getDownloadManagerToolbarForTests()
.getMenu()
.performIdentifierAction(
R.id.selection_mode_delete_menu_id, 0)));
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that items are temporarily removed from the adapter. The two selected items,
// one duplicate item, and one date bucket should be removed.
Assert.assertEquals(11, mAdapter.getItemCount());
Assert.assertEquals("1.00 GB downloaded", mSpaceUsedDisplay.getText());
// Click "Undo" on the snackbar.
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final View rootView = mUi.getView().getRootView();
Assert.assertNotNull(rootView.findViewById(R.id.snackbar));
ThreadUtils.runOnUiThread(
(Runnable) () -> rootView.findViewById(R.id.snackbar_button).callOnClick());
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that items are restored.
Assert.assertEquals(
0, mStubbedProvider.getDownloadDelegate().removeDownloadCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getOfflineContentProvider().deleteItemCallback.getCallCount());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
Assert.assertEquals(15, mAdapter.getItemCount());
Assert.assertEquals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
@DisabledTest(message = "crbug.com/855389")
@Test
@MediumTest
@RetryOnFailure
public void testUndoDeleteFromMenu() throws Exception {
// Adapter positions:
// 0 = space display
// 1 = date
// 2 = download item #7
// 3 = download item #8
// 4 = date
// 5 = download item #6
// 6 = offline page #3
SnackbarManager.setDurationForTesting(5000);
// Add duplicate items.
int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final DownloadItem item7 = StubbedProvider.createDownloadItem(7, "20161021 07:28");
final DownloadItem item8 = StubbedProvider.createDownloadItem(8, "20161021 17:28");
ThreadUtils.runOnUiThread(() -> {
mAdapter.onDownloadItemCreated(item7);
mAdapter.onDownloadItemCreated(item8);
});
// The criteria is needed because an AsyncTask is fired to update the space display, which
// can result in either 1 or 2 updates.
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
});
Assert.assertEquals(15, mAdapter.getItemCount());
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
// Simulate a delete context menu action on the item.
simulateContextMenu(2, R.string.delete);
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that items are temporarily removed from the adapter. The two selected items,
// one duplicate item, and one date bucket should be removed.
Assert.assertEquals(12, mAdapter.getItemCount());
Assert.assertEquals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
// Click "Undo" on the snackbar.
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final View rootView = mUi.getView().getRootView();
Assert.assertNotNull(rootView.findViewById(R.id.snackbar));
ThreadUtils.runOnUiThread(
(Runnable) () -> rootView.findViewById(R.id.snackbar_button).callOnClick());
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that items are restored.
Assert.assertEquals(
0, mStubbedProvider.getDownloadDelegate().removeDownloadCallback.getCallCount());
Assert.assertEquals(
0, mStubbedProvider.getOfflineContentProvider().deleteItemCallback.getCallCount());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
Assert.assertEquals(15, mAdapter.getItemCount());
Assert.assertEquals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
@DisabledTest(message = "crbug.com/855389")
@Test
@MediumTest
@RetryOnFailure
public void testUndoDeleteDuplicatesSelected() throws Exception {
// Adapter positions:
// 0 = space display
// 1 = date
// 2 = download item #7
// 3 = download item #8
// ....
SnackbarManager.setDurationForTesting(5000);
// Add duplicate items.
int callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final DownloadItem item7 = StubbedProvider.createDownloadItem(7, "20161021 07:28");
final DownloadItem item8 = StubbedProvider.createDownloadItem(8, "20161021 17:28");
ThreadUtils.runOnUiThread(() -> {
mAdapter.onDownloadItemCreated(item7);
mAdapter.onDownloadItemCreated(item8);
});
// The criteria is needed because an AsyncTask is fired to update the space display, which
// can result in either 1 or 2 updates.
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
CriteriaHelper.pollUiThread(new Criteria() {
@Override
public boolean isSatisfied() {
return TextUtils.equals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
});
// Select download item #7 and download item #8.
toggleItemSelection(2);
toggleItemSelection(3);
Assert.assertEquals(15, mAdapter.getItemCount());
// Click the delete button.
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
ThreadUtils.runOnUiThread(
()
-> Assert.assertTrue(
mUi.getDownloadManagerToolbarForTests()
.getMenu()
.performIdentifierAction(
R.id.selection_mode_delete_menu_id, 0)));
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that the two items and their date bucket are temporarily removed from the adapter.
Assert.assertEquals(12, mAdapter.getItemCount());
Assert.assertEquals("6.00 GB downloaded", mSpaceUsedDisplay.getText());
// Click "Undo" on the snackbar.
callCount = mAdapterObserver.onSpaceDisplayUpdatedCallback.getCallCount();
final View rootView = mUi.getView().getRootView();
Assert.assertNotNull(rootView.findViewById(R.id.snackbar));
ThreadUtils.runOnUiThread(
(Runnable) () -> rootView.findViewById(R.id.snackbar_button).callOnClick());
mAdapterObserver.onSpaceDisplayUpdatedCallback.waitForCallback(callCount);
// Assert that items are restored.
Assert.assertEquals(15, mAdapter.getItemCount());
Assert.assertEquals("6.50 GB downloaded", mSpaceUsedDisplay.getText());
}
@Test
@MediumTest
@DisableFeatures("OfflinePagesSharing")
@FlakyTest(message = "crbug.com/855167")
public void testShareFiles() throws Exception {
// Adapter positions:
// 0 = space display
// 1 = date
// 2 = download item #6
// 3 = offline page #3
// 4 = date
// 5 = download item #3
// 6 = download item #4
// 7 = download item #5
// 8 = date
// 9 = download item #0
// 10 = download item #1
// 11 = download item #2
// Select an image, download item #6.
toggleItemSelection(2);
Intent shareIntent = DownloadUtils.createShareIntent(
mUi.getBackendProvider().getSelectionDelegate().getSelectedItemsAsList(), null);
Assert.assertEquals("Incorrect intent action", Intent.ACTION_SEND, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "image/png", shareIntent.getType());
Assert.assertNotNull(
"Intent expected to have stream", shareIntent.getExtras().get(Intent.EXTRA_STREAM));
Assert.assertNull("Intent not expected to have parcelable ArrayList",
shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM));
// Scroll to ensure the item at position 8 is visible.
ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(9));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Select another image, download item #0.
toggleItemSelection(9);
shareIntent = DownloadUtils.createShareIntent(
mUi.getBackendProvider().getSelectionDelegate().getSelectedItemsAsList(), null);
Assert.assertEquals(
"Incorrect intent action", Intent.ACTION_SEND_MULTIPLE, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "image/*", shareIntent.getType());
Assert.assertEquals("Intent expected to have parcelable ArrayList", 2,
shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM).size());
// Scroll to ensure the item at position 5 is visible.
ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(6));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Select non-image item, download item #4.
toggleItemSelection(6);
shareIntent = DownloadUtils.createShareIntent(
mUi.getBackendProvider().getSelectionDelegate().getSelectedItemsAsList(), null);
Assert.assertEquals(
"Incorrect intent action", Intent.ACTION_SEND_MULTIPLE, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "*/*", shareIntent.getType());
Assert.assertEquals("Intent expected to have parcelable ArrayList", 3,
shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM).size());
// Scroll to ensure the item at position 2 is visible.
ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(3));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Select an offline page #3.
toggleItemSelection(3);
shareIntent = DownloadUtils.createShareIntent(
mUi.getBackendProvider().getSelectionDelegate().getSelectedItemsAsList(), null);
Assert.assertEquals(
"Incorrect intent action", Intent.ACTION_SEND_MULTIPLE, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "*/*", shareIntent.getType());
Assert.assertEquals("Intent expected to have parcelable ArrayList", 3,
shareIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM).size());
Assert.assertEquals("Intent expected to have plain text for offline page URL",
"https://thangs.com",
IntentUtils.safeGetStringExtra(shareIntent, Intent.EXTRA_TEXT));
}
// TODO(carlosk): OfflineItems used here come from StubbedProvider so this might not be the best
// place to test peer-2-peer sharing.
@DisabledTest(message = "crbug.com/855389")
@Test
@MediumTest
@EnableFeatures("OfflinePagesSharing")
public void testShareOfflinePageWithP2PSharingEnabled() throws Exception {
// Scroll to ensure the item at position 2 is visible.
ThreadUtils.runOnUiThread(() -> mRecyclerView.scrollToPosition(3));
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
// Select the offline page located at position #3.
toggleItemSelection(3);
List<DownloadHistoryItemWrapper> selectedItems =
mUi.getBackendProvider().getSelectionDelegate().getSelectedItemsAsList();
Assert.assertEquals("There should be only one item selected", 1, selectedItems.size());
Intent shareIntent = DownloadUtils.createShareIntent(selectedItems, null);
Assert.assertEquals("Incorrect intent action", Intent.ACTION_SEND, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "*/*", shareIntent.getType());
Assert.assertNotNull("Intent expected to have parcelable ArrayList",
shareIntent.getParcelableExtra(Intent.EXTRA_STREAM));
Assert.assertEquals("Intent expected to have parcelable Uri",
"file:///data/fake_path/Downloads/4",
shareIntent.getParcelableExtra(Intent.EXTRA_STREAM).toString());
Assert.assertNull("Intent expected to not have any text for offline page",
IntentUtils.safeGetStringExtra(shareIntent, Intent.EXTRA_TEXT));
// Pass a map that contains a new file path.
HashMap<String, String> newFilePathMap = new HashMap<String, String>();
newFilePathMap.put(((OfflineItemWrapper) selectedItems.get(0)).getId(),
"/data/new_fake_path/Downloads/4");
shareIntent = DownloadUtils.createShareIntent(selectedItems, newFilePathMap);
Assert.assertEquals("Incorrect intent action", Intent.ACTION_SEND, shareIntent.getAction());
Assert.assertEquals("Incorrect intent mime type", "*/*", shareIntent.getType());
Assert.assertNotNull("Intent expected to have parcelable ArrayList",
shareIntent.getParcelableExtra(Intent.EXTRA_STREAM));
Assert.assertEquals("Intent expected to have parcelable Uri",
"file:///data/new_fake_path/Downloads/4",
shareIntent.getParcelableExtra(Intent.EXTRA_STREAM).toString());
Assert.assertNull("Intent expected to not have any text for offline page",
IntentUtils.safeGetStringExtra(shareIntent, Intent.EXTRA_TEXT));
}
@Test
@MediumTest
@DisableFeatures(ChromeFeatureList.DOWNLOADS_LOCATION_CHANGE)
public void testLongClickItem() throws Exception {
// The selection toolbar should not be showing.
onView(withContentDescription("Cancel selection")).check(doesNotExist());
onView(withId(R.id.close_menu_id)).check(matches(isDisplayed()));
onView(withId(R.id.selection_mode_number)).check(matches(not(isDisplayed())));
onView(withId(org.chromium.chrome.download.R.id.selection_mode_share_menu_id))
.check(doesNotExist());
onView(withId(R.id.selection_mode_delete_menu_id)).check(doesNotExist());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
// Select an item.
onView(withText("huge_image.png")).perform(longClick());
// The toolbar should flip states to allow doing things with the selected items.
onView(withId(R.id.close_menu_id)).check(doesNotExist());
onView(withId(R.id.selection_mode_number)).check(matches(isDisplayed()));
onView(withId(org.chromium.chrome.download.R.id.selection_mode_share_menu_id))
.check(matches(isDisplayed()));
onView(withId(R.id.selection_mode_delete_menu_id)).check(matches(isDisplayed()));
Assert.assertTrue(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
// Deselect the same item.
onView(withText("huge_image.png")).perform(longClick());
// The toolbar should flip back.
onView(withContentDescription("Cancel selection")).check(doesNotExist());
onView(withId(R.id.close_menu_id)).check(matches(isDisplayed()));
onView(withId(R.id.selection_mode_number)).check(matches(not(isDisplayed())));
onView(withId(org.chromium.chrome.download.R.id.selection_mode_share_menu_id))
.check(doesNotExist());
onView(withId(R.id.selection_mode_delete_menu_id)).check(doesNotExist());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
}
@Test
@MediumTest
@DisableFeatures(ChromeFeatureList.DOWNLOADS_LOCATION_CHANGE)
public void testSearchView() throws Exception {
final DownloadManagerToolbar toolbar = mUi.getDownloadManagerToolbarForTests();
onView(withId(R.id.search_text)).check(matches(not(isDisplayed())));
onView(withText("huge_image.png")).perform(longClick());
Assert.assertTrue(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
int callCount = mAdapterObserver.onSelectionCallback.getCallCount();
ThreadUtils.runOnUiThreadBlocking(
(Runnable) () -> toolbar.getMenu().performIdentifierAction(R.id.search_menu_id, 0));
// The selection should be cleared when a search is started.
mAdapterObserver.onSelectionCallback.waitForCallback(callCount, 1);
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
onView(withId(R.id.search_text)).check(matches(isDisplayed()));
// Select an item and assert that the search view is no longer showing.
onView(withText("huge_image.png")).perform(longClick());
Assert.assertTrue(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
onView(withId(R.id.search_text)).check(matches(not(isDisplayed())));
// Clear the selection and assert that the search view is showing again.
onView(withText("huge_image.png")).perform(longClick());
Assert.assertFalse(mStubbedProvider.getSelectionDelegate().isSelectionEnabled());
onView(withId(R.id.search_text)).check(matches(isDisplayed()));
// Close the search view.
onView(withContentDescription("Go back")).perform(click());
onView(withId(R.id.search_text)).check(matches(not(isDisplayed())));
}
@Test
@MediumTest
@DisableFeatures(ChromeFeatureList.DOWNLOADS_LOCATION_CHANGE)
public void testSpinner() throws Exception {
// Open spinner.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
// Check all TextViews displayed.
onView(withText("All")).check(matches(isDisplayed()));
onView(withText("Pages")).check(matches(isDisplayed()));
onView(withText("Video")).check(matches(isDisplayed()));
onView(withText("Audio")).check(matches(isDisplayed()));
onView(withText("Images")).check(matches(isDisplayed()));
onView(withText("Documents")).check(matches(isDisplayed()));
onView(withText("Other")).check(matches(isDisplayed()));
// Click Pages.
onView(withText("Pages")).perform(click());
onView(withText("page 4")).check(matches(isDisplayed()));
// Click Video.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
onView(withText("Video")).perform(click());
onView(withText("four.webm")).check(matches(isDisplayed()));
// Click Audio.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
onView(withText("Audio")).perform(click());
onView(withText("five.mp3")).check(matches(isDisplayed()));
onView(withText("six.mp3")).check(matches(isDisplayed()));
// Click Images.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
onView(withText("Images")).perform(click());
onView(withText("huge_image.png")).check(matches(isDisplayed()));
onView(withText("first_file.jpg")).check(matches(isDisplayed()));
onView(withText("second_file.gif")).check(matches(isDisplayed()));
// Click Documents.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
onView(withText("Documents")).perform(click());
onView(withText("third_file")).check(matches(isDisplayed()));
// Click Other.
onView(withId(org.chromium.chrome.download.R.id.spinner)).perform(click());
onView(withText("Other")).perform(click());
onView(withText("No downloads here")).check(matches(isDisplayed()));
}
private DownloadActivity startDownloadActivity() throws Exception {
// Load up the downloads lists.
DownloadItem item0 = StubbedProvider.createDownloadItem(0, "19551112 06:38");
DownloadItem item1 = StubbedProvider.createDownloadItem(1, "19551112 06:38");
DownloadItem item2 = StubbedProvider.createDownloadItem(2, "19551112 06:38");
DownloadItem item3 = StubbedProvider.createDownloadItem(3, "19851026 09:00");
DownloadItem item4 = StubbedProvider.createDownloadItem(4, "19851026 09:00");
DownloadItem item5 = StubbedProvider.createDownloadItem(5, "19851026 09:00");
DownloadItem item6 = StubbedProvider.createDownloadItem(6, "20151021 07:28");
OfflineItem item7 = StubbedProvider.createOfflineItem(3, "20151021 07:28");
mStubbedProvider.getDownloadDelegate().regularItems.add(item0);
mStubbedProvider.getDownloadDelegate().regularItems.add(item1);
mStubbedProvider.getDownloadDelegate().regularItems.add(item2);
mStubbedProvider.getDownloadDelegate().regularItems.add(item3);
mStubbedProvider.getDownloadDelegate().regularItems.add(item4);
mStubbedProvider.getDownloadDelegate().regularItems.add(item5);
mStubbedProvider.getDownloadDelegate().regularItems.add(item6);
mStubbedProvider.getOfflineContentProvider().items.add(item7);
// Start the activity up.
Intent intent = new Intent();
intent.setClass(InstrumentationRegistry.getTargetContext(), DownloadActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return mActivityTestRule.launchActivity(intent);
}
private void clickOnFilter(final DownloadManagerUi ui, final int position) throws Exception {
int previousCount = mAdapterObserver.onChangedCallback.getCallCount();
final Spinner spinner = mUi.getDownloadManagerToolbarForTests().getSpinnerForTests();
ThreadUtils.runOnUiThread(() -> {
spinner.performClick();
spinner.setSelection(position);
});
mAdapterObserver.onChangedCallback.waitForCallback(previousCount);
}
private void toggleItemSelection(int position) throws Exception {
int callCount = mAdapterObserver.onSelectionCallback.getCallCount();
final DownloadItemView itemView = getView(position);
ThreadUtils.runOnUiThread((Runnable) () -> itemView.performLongClick());
mAdapterObserver.onSelectionCallback.waitForCallback(callCount, 1);
}
private void simulateContextMenu(int position, @StringRes int text) throws Exception {
final DownloadItemView view = getView(position);
ThreadUtils.runOnUiThread((Runnable) () -> {
Item[] items = view.getItems();
for (Item item : items) {
if (item.getTextId() == text) {
view.onItemSelected(item);
return;
}
}
throw new IllegalStateException("Context menu option not found " + text);
});
}
private DownloadItemView getView(int position) throws Exception {
int callCount = mAdapterObserver.onSelectionCallback.getCallCount();
ViewHolder mostRecentHolder = mRecyclerView.findViewHolderForAdapterPosition(position);
Assert.assertTrue(mostRecentHolder instanceof DownloadHistoryItemViewHolder);
return ((DownloadHistoryItemViewHolder) mostRecentHolder).getItemView();
}
}