TrashbinActivity:

- moved to ViewBindings
- add UI test for error/view

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2020-07-27 13:32:13 +02:00 committed by Andy Scherzinger
parent 607ed3115e
commit f7ab5d90dc
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
7 changed files with 222 additions and 86 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,86 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2020 Tobias Kaminsky
* Copyright (C) 2020 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin
import androidx.test.espresso.intent.rule.IntentsTestRule
import com.facebook.testing.screenshot.Screenshot
import com.owncloud.android.AbstractIT
import com.owncloud.android.utils.ScreenshotTest
import org.junit.Rule
import org.junit.Test
class TrashbinActivityIT : AbstractIT() {
enum class TestCase {
ERROR, EMPTY, FILES
}
@get:Rule
var activityRule = IntentsTestRule(TrashbinActivity::class.java, true, false)
@Test
@ScreenshotTest
fun error() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
val trashbinRepository = TrashbinLocalRepository(TestCase.ERROR)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
shortSleep()
Screenshot.snapActivity(sut).record()
}
@Test
@ScreenshotTest
fun files() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
val trashbinRepository = TrashbinLocalRepository(TestCase.FILES)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
shortSleep()
Screenshot.snapActivity(sut).record()
}
@Test
@ScreenshotTest
fun empty() {
val sut: TrashbinActivity = activityRule.launchActivity(null)
val trashbinRepository = TrashbinLocalRepository(TestCase.EMPTY)
sut.trashbinPresenter = TrashbinPresenter(trashbinRepository, sut)
sut.runOnUiThread { sut.loadFolder() }
shortSleep()
Screenshot.snapActivity(sut).record()
}
}

View file

@ -0,0 +1,84 @@
/*
*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2020 Tobias Kaminsky
* Copyright (C) 2020 Nextcloud GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.ui.trashbin
import com.owncloud.android.R
import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile
import com.owncloud.android.ui.trashbin.TrashbinRepository.LoadFolderCallback
class TrashbinLocalRepository(val testCase: TrashbinActivityIT.TestCase) : TrashbinRepository {
override fun emptyTrashbin(callback: TrashbinRepository.OperationCallback?) {
TODO("Not yet implemented")
}
override fun restoreFile(file: TrashbinFile?, callback: TrashbinRepository.OperationCallback?) {
TODO("Not yet implemented")
}
override fun removeTrashbinFile(file: TrashbinFile?, callback: TrashbinRepository.OperationCallback?) {
TODO("Not yet implemented")
}
@Suppress("MagicNumber")
override fun getFolder(remotePath: String?, callback: LoadFolderCallback?) {
when (testCase) {
TrashbinActivityIT.TestCase.ERROR -> callback?.onError(R.string.trashbin_loading_failed)
TrashbinActivityIT.TestCase.FILES -> {
val files = ArrayList<Any>()
files.add(
TrashbinFile(
"test.md",
"text/markdown",
"/trashbin/test.md",
"subFolder/test.md",
1395847838, // random date
1395847908 // random date
)
)
files.add(
TrashbinFile(
"image.jpg",
"image/jpeg",
"/trashbin/image.jpg",
"image.jpg",
1395841858, // random date
1395837858 // random date
)
)
files.add(
TrashbinFile(
"folder",
"DIR",
"/trashbin/folder/",
"folder",
1395347858, // random date
1395849858 // random date
)
)
callback?.onSuccess(files)
}
TrashbinActivityIT.TestCase.EMPTY -> callback?.onSuccess(ArrayList<Any>())
}
}
}

View file

@ -28,11 +28,9 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.account.User;
@ -40,6 +38,7 @@ import com.nextcloud.client.di.Injectable;
import com.nextcloud.client.network.ClientFactory;
import com.nextcloud.client.preferences.AppPreferences;
import com.owncloud.android.R;
import com.owncloud.android.databinding.TrashbinActivityBinding;
import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile;
import com.owncloud.android.ui.EmptyRecyclerView;
import com.owncloud.android.ui.activity.FileActivity;
@ -55,13 +54,9 @@ import java.util.List;
import javax.inject.Inject;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import static com.owncloud.android.utils.DisplayUtils.openSortingOrderDialogFragment;
@ -74,44 +69,16 @@ public class TrashbinActivity extends FileActivity implements
TrashbinContract.View,
Injectable {
@BindView(R.id.empty_list_progress)
public View emptyListProgress;
@BindView(R.id.empty_list_view)
public View emptyListView;
@BindView(R.id.empty_list_view_text)
public TextView emptyContentMessage;
@BindView(R.id.empty_list_view_headline)
public TextView emptyContentHeadline;
@BindView(R.id.empty_list_icon)
public ImageView emptyContentIcon;
@BindView(android.R.id.list)
public EmptyRecyclerView recyclerView;
@BindView(R.id.swipe_containing_list)
public SwipeRefreshLayout swipeListRefreshLayout;
@BindView(R.id.sort_button)
public MaterialButton sortButton;
@BindString(R.string.trashbin_empty_headline)
public String noResultsHeadline;
@BindString(R.string.trashbin_empty_message)
public String noResultsMessage;
@Inject AppPreferences preferences;
@Inject CurrentAccountProvider accountProvider;
@Inject ClientFactory clientFactory;
private Unbinder unbinder;
private TrashbinListAdapter trashbinListAdapter;
private TrashbinPresenter trashbinPresenter;
@VisibleForTesting
TrashbinPresenter trashbinPresenter;
private boolean active;
private TrashbinActivityBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -119,8 +86,10 @@ public class TrashbinActivity extends FileActivity implements
final User user = accountProvider.getUser();
final RemoteTrashbinRepository trashRepository = new RemoteTrashbinRepository(user, clientFactory);
trashbinPresenter = new TrashbinPresenter(trashRepository, this);
setContentView(R.layout.trashbin_activity);
unbinder = ButterKnife.bind(this);
binding = TrashbinActivityBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setupToolbar();
findViewById(R.id.sort_list_button_group).setVisibility(View.VISIBLE);
findViewById(R.id.switch_grid_view_button).setVisibility(View.GONE);
@ -136,14 +105,14 @@ public class TrashbinActivity extends FileActivity implements
}
private void setupContent() {
recyclerView = findViewById(android.R.id.list);
recyclerView.setEmptyView(emptyListView);
emptyListView.setVisibility(View.GONE);
emptyContentIcon.setImageResource(R.drawable.ic_delete);
emptyContentIcon.setVisibility(View.VISIBLE);
emptyContentHeadline.setText(noResultsHeadline);
emptyContentMessage.setText(noResultsMessage);
emptyContentMessage.setVisibility(View.VISIBLE);
EmptyRecyclerView recyclerView = binding.list;
recyclerView.setEmptyView(binding.emptyList.emptyListView);
binding.emptyList.emptyListView.setVisibility(View.GONE);
binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_delete);
binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
binding.emptyList.emptyListViewHeadline.setText(getString(R.string.trashbin_empty_headline));
binding.emptyList.emptyListViewText.setText(getString(R.string.trashbin_empty_message));
binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE);
trashbinListAdapter = new TrashbinListAdapter(
this,
@ -157,21 +126,21 @@ public class TrashbinActivity extends FileActivity implements
recyclerView.setHasFooter(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
ThemeUtils.colorSwipeRefreshLayout(this, swipeListRefreshLayout);
swipeListRefreshLayout.setOnRefreshListener(this::loadFolder);
ThemeUtils.colorSwipeRefreshLayout(this, binding.swipeContainingList);
binding.swipeContainingList.setOnRefreshListener(this::loadFolder);
sortButton.setOnClickListener(l ->
openSortingOrderDialogFragment(getSupportFragmentManager(),
preferences.getSortOrderByType(
FileSortOrder.Type.trashBinView,
FileSortOrder.sort_new_to_old))
);
findViewById(R.id.sort_button).setOnClickListener(l ->
openSortingOrderDialogFragment(getSupportFragmentManager(),
preferences.getSortOrderByType(
FileSortOrder.Type.trashBinView,
FileSortOrder.sort_new_to_old))
);
loadFolder();
}
private void loadFolder() {
swipeListRefreshLayout.setRefreshing(true);
protected void loadFolder() {
binding.swipeContainingList.setRefreshing(true);
trashbinPresenter.loadFolder();
}
@ -209,12 +178,6 @@ public class TrashbinActivity extends FileActivity implements
return retval;
}
@Override
public void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
@Override
public void onOverflowIconClicked(TrashbinFile file, View view) {
PopupMenu popup = new PopupMenu(this, view);
@ -273,6 +236,7 @@ public class TrashbinActivity extends FileActivity implements
@Override
public void onSortingOrderChosen(FileSortOrder sortOrder) {
TextView sortButton = findViewById(R.id.sort_button);
sortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder));
trashbinListAdapter.setSortOrder(sortOrder);
}
@ -281,8 +245,8 @@ public class TrashbinActivity extends FileActivity implements
public void showTrashbinFolder(List<Object> trashbinFiles) {
if (active) {
trashbinListAdapter.setTrashbinFiles(trashbinFiles, true);
swipeListRefreshLayout.setRefreshing(false);
emptyListProgress.setVisibility(View.GONE);
binding.swipeContainingList.setRefreshing(false);
binding.emptyList.emptyListProgress.setVisibility(View.GONE);
}
}
@ -301,8 +265,9 @@ public class TrashbinActivity extends FileActivity implements
@Override
public void showSnackbarError(int message, TrashbinFile file) {
if (active) {
swipeListRefreshLayout.setRefreshing(false);
Snackbar.make(recyclerView, String.format(getString(message), file.getFileName()), Snackbar.LENGTH_LONG)
binding.swipeContainingList.setRefreshing(false);
Snackbar.make(binding.list,
String.format(getString(message), file.getFileName()), Snackbar.LENGTH_LONG)
.show();
}
}
@ -310,20 +275,17 @@ public class TrashbinActivity extends FileActivity implements
@Override
public void showError(int message) {
if (active) {
swipeListRefreshLayout.setRefreshing(false);
binding.swipeContainingList.setRefreshing(false);
if (emptyContentMessage != null) {
emptyContentHeadline.setText(R.string.common_error);
emptyContentIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(),
R.drawable.ic_list_empty_error,
null));
emptyContentMessage.setText(message);
emptyContentMessage.setVisibility(View.VISIBLE);
emptyContentIcon.setVisibility(View.VISIBLE);
emptyListView.setVisibility(View.VISIBLE);
emptyListProgress.setVisibility(View.GONE);
}
binding.emptyList.emptyListViewHeadline.setText(R.string.common_error);
binding.emptyList.emptyListIcon.setImageDrawable(ResourcesCompat.getDrawable(getResources(),
R.drawable.ic_list_empty_error,
null));
binding.emptyList.emptyListViewText.setText(message);
binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE);
binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE);
binding.emptyList.emptyListView.setVisibility(View.VISIBLE);
binding.emptyList.emptyListProgress.setVisibility(View.GONE);
}
}
}

View file

@ -33,13 +33,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/toolbar_standard" />
<include
android:id="@+id/toolbar_standard_include"
layout="@layout/toolbar_standard" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/list_fragment_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/appbar">
android:layout_below="@id/toolbar_standard_include">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_containing_list"
@ -52,10 +54,12 @@
<com.owncloud.android.ui.EmptyRecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<include layout="@layout/empty_list"/>
<include
android:id="@+id/empty_list"
layout="@layout/empty_list" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>