aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/build.gradle6
-rw-r--r--app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java112
-rw-r--r--app/src/androidTest/java/org/connectbot/StartupTest.java34
-rw-r--r--app/src/main/java/org/connectbot/HostListActivity.java4
4 files changed, 93 insertions, 63 deletions
diff --git a/app/build.gradle b/app/build.gradle
index e75294e..dd16ccb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -76,6 +76,12 @@ android {
androidTestCompile('com.android.support.test.espresso:espresso-intents:2.2') {
exclude module: "support-annotations"
}
+ androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){
+ exclude module: 'support-annotations'
+ exclude module: 'support-v4'
+ exclude module: 'support-v13'
+ exclude module: 'recyclerview-v7'
+ }
}
buildTypes {
diff --git a/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java b/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java
index 85a071d..a44e88c 100644
--- a/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java
+++ b/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java
@@ -2,7 +2,10 @@ package org.connectbot;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
import android.support.test.espresso.matcher.BoundedMatcher;
+import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -10,22 +13,18 @@ import android.widget.TextView;
import org.connectbot.bean.HostBean;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.CoreMatchers.allOf;
+import org.junit.Assert;
public class ConnectbotMatchers {
/**
* Matches the nickname of a {@link HostBean}.
*/
@NonNull
- public static Matcher<Object> withHostNickname(final String content) {
- return new BoundedMatcher<Object, HostBean>(HostBean.class) {
+ public static Matcher<RecyclerView.ViewHolder> withHostNickname(final String content) {
+ return new BoundedMatcher<RecyclerView.ViewHolder, HostListActivity.HostAdapter.ViewHolder>(HostListActivity.HostAdapter.ViewHolder.class) {
@Override
- public boolean matchesSafely(HostBean host) {
- return host.getNickname().matches(content);
+ public boolean matchesSafely(HostListActivity.HostAdapter.ViewHolder holder) {
+ return holder.host.getNickname().matches(content);
}
@Override
@@ -35,63 +34,90 @@ public class ConnectbotMatchers {
};
}
- /**
- * Matches the drawable state on an ImageView that is set with setImageState.
- */
@NonNull
- public static Matcher<View> withDrawableState(final int expectedState) {
- return new TypeSafeMatcher<View>() {
+ public static Matcher<RecyclerView.ViewHolder> withConnectedHost() {
+ return new BoundedMatcher<RecyclerView.ViewHolder, HostListActivity.HostAdapter.ViewHolder>(HostListActivity.HostAdapter.ViewHolder.class) {
@Override
- public boolean matchesSafely(View view) {
- if (!(view instanceof ImageView)) {
- return false;
- }
-
- int[] states = view.getDrawableState();
- for (int state : states) {
- if (state == expectedState) {
- return true;
- }
- }
- return false;
+ public boolean matchesSafely(HostListActivity.HostAdapter.ViewHolder holder) {
+ return hasDrawableState(holder.icon, android.R.attr.state_checked);
}
@Override
public void describeTo(Description description) {
- description.appendText("with drawable state '" + expectedState + "'");
+ description.appendText("is displayed");
}
};
}
@NonNull
- public static Matcher<View> withTextColor(@ColorInt final int expectedColor) {
- return new TypeSafeMatcher<View>() {
+ public static Matcher<RecyclerView.ViewHolder> withDisconnectedHost() {
+ return new BoundedMatcher<RecyclerView.ViewHolder, HostListActivity.HostAdapter.ViewHolder>(HostListActivity.HostAdapter.ViewHolder.class) {
@Override
- public boolean matchesSafely(View view) {
- if (!(view instanceof TextView)) {
- return false;
- }
-
- TextView tv = (TextView) view;
- return tv.getCurrentTextColor() == expectedColor;
+ public boolean matchesSafely(HostListActivity.HostAdapter.ViewHolder holder) {
+ return hasDrawableState(holder.icon, android.R.attr.state_expanded);
}
@Override
public void describeTo(Description description) {
- description.appendText("with color '" + Integer.toHexString(expectedColor) + "'");
+ description.appendText("is displayed");
}
};
}
@NonNull
- public static Matcher<View> hostDisconnected() {
- return hasDescendant(allOf(withId(android.R.id.icon),
- withDrawableState(android.R.attr.state_expanded)));
+ public static Matcher<RecyclerView.ViewHolder> withColoredText(@ColorInt final int expectedColor) {
+ return new BoundedMatcher<RecyclerView.ViewHolder, HostListActivity.HostAdapter.ViewHolder>(HostListActivity.HostAdapter.ViewHolder.class) {
+ @Override
+ public boolean matchesSafely(HostListActivity.HostAdapter.ViewHolder holder) {
+ return hasTextColor(holder.nickname, expectedColor);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is displayed");
+ }
+ };
+ }
+
+ private static boolean hasDrawableState(View view, final int expectedState) {
+ if (!(view instanceof ImageView)) {
+ return false;
+ }
+
+ int[] states = view.getDrawableState();
+ for (int state : states) {
+ if (state == expectedState) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasTextColor(View view, @ColorInt final int expectedColor) {
+ if (!(view instanceof TextView)) {
+ return false;
+ }
+
+ TextView tv = (TextView) view;
+ return tv.getCurrentTextColor() == expectedColor;
}
@NonNull
- public static Matcher<View> hostConnected() {
- return hasDescendant(allOf(withId(android.R.id.icon),
- withDrawableState(android.R.attr.state_checked)));
+ public static ViewAssertion hasHolderItem(final Matcher<RecyclerView.ViewHolder> viewHolderMatcher) {
+ return new ViewAssertion() {
+ @Override public void check(View view, NoMatchingViewException e) {
+ if (!(view instanceof RecyclerView)) {
+ throw e;
+ }
+
+ boolean hasMatch = false;
+ RecyclerView rv = (RecyclerView) view;
+ for (int i = 0; i < rv.getChildCount(); i++) {
+ RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(i);
+ hasMatch |= viewHolderMatcher.matches(vh);
+ }
+ Assert.assertTrue(hasMatch);
+ }
+ };
}
}
diff --git a/app/src/androidTest/java/org/connectbot/StartupTest.java b/app/src/androidTest/java/org/connectbot/StartupTest.java
index 14eabff..72259eb 100644
--- a/app/src/androidTest/java/org/connectbot/StartupTest.java
+++ b/app/src/androidTest/java/org/connectbot/StartupTest.java
@@ -16,6 +16,7 @@ import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.action.CloseKeyboardAction;
+import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.espresso.intent.Intents;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
@@ -36,10 +37,11 @@ import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static org.connectbot.ConnectbotMatchers.hostConnected;
-import static org.connectbot.ConnectbotMatchers.hostDisconnected;
+import static org.connectbot.ConnectbotMatchers.hasHolderItem;
+import static org.connectbot.ConnectbotMatchers.withColoredText;
+import static org.connectbot.ConnectbotMatchers.withConnectedHost;
+import static org.connectbot.ConnectbotMatchers.withDisconnectedHost;
import static org.connectbot.ConnectbotMatchers.withHostNickname;
-import static org.connectbot.ConnectbotMatchers.withTextColor;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
@@ -65,16 +67,16 @@ public class StartupTest {
onView(withId(R.id.console_flip)).perform(closeSoftKeyboard(), pressBack());
// Make sure we're still connected.
- onData(withHostNickname("Local")).inAdapterView(withId(android.R.id.list))
- .check(matches(hostConnected()))
- .perform(longClick());
+ onView(withId(R.id.list))
+ .check(hasHolderItem(allOf(withHostNickname("Local"), withConnectedHost())))
+ .perform(RecyclerViewActions.actionOnHolderItem(
+ allOf(withHostNickname("Local"), withConnectedHost()), longClick()));
// Click on the disconnect context menu item.
onView(withText(R.string.list_host_disconnect)).check(matches(isDisplayed())).perform(click());
// Now make sure we're disconnected.
- onData(withHostNickname("Local")).inAdapterView(withId(android.R.id.list))
- .check(matches(hostDisconnected()));
+ onView(withId(R.id.list)).check(hasHolderItem(allOf(withHostNickname("Local"), withDisconnectedHost())));
}
@Test
@@ -87,15 +89,13 @@ public class StartupTest {
onView(withText(R.string.list_host_disconnect)).check(matches(isDisplayed())).perform(click());
// Now make sure we're disconnected.
- onData(withHostNickname("Local")).inAdapterView(withId(android.R.id.list))
- .check(matches(hostDisconnected()));
+ onView(withId(R.id.list)).check(hasHolderItem(allOf(withHostNickname("Local"), withDisconnectedHost())));
}
@Test
public void localConnectionCanDelete() {
startNewLocalConnectionAndGoBack("Local");
- onData(withHostNickname("Local")).inAdapterView(withId(android.R.id.list))
- .perform(longClick());
+ onView(withId(R.id.list)).perform(RecyclerViewActions.actionOnHolderItem(withHostNickname("Local"), longClick()));
onView(withText(R.string.list_host_delete)).perform(click());
onView(withText(R.string.delete_pos)).perform(click());
}
@@ -113,8 +113,7 @@ public class StartupTest {
*/
private void changeColor(String hostName, @ColorRes int color, @StringRes int stringForColor) {
// Bring up the context menu.
- onData(withHostNickname(hostName)).inAdapterView(withId(android.R.id.list))
- .perform(longClick());
+ onView(withId(R.id.list)).perform(RecyclerViewActions.actionOnHolderItem(withHostNickname(hostName), longClick()));
onView(withText(R.string.list_host_edit)).perform(click());
// Click on the color category and select the desired one.
@@ -125,16 +124,13 @@ public class StartupTest {
onView(withText(R.string.hostpref_color_title)).perform(pressBack());
Resources res = InstrumentationRegistry.getTargetContext().getResources();
- onData(withHostNickname(hostName)).inAdapterView(withId(android.R.id.list))
- .check(matches(hasDescendant(allOf(withId(android.R.id.text1),
- withTextColor(res.getColor(color))))));
+ onView(withId(R.id.list)).check(hasHolderItem(withColoredText(res.getColor(color))));
}
private void startNewLocalConnectionAndGoBack(String name) {
startNewLocalConnection(name);
onView(withId(R.id.console_flip)).perform(closeSoftKeyboard(), pressBack());
- onData(withHostNickname(name)).inAdapterView(withId(android.R.id.list))
- .check(matches(isDisplayed()));
+ onView(withId(R.id.list)).check(hasHolderItem(withHostNickname(name)));
}
private void startNewLocalConnection() {
diff --git a/app/src/main/java/org/connectbot/HostListActivity.java b/app/src/main/java/org/connectbot/HostListActivity.java
index 38afb62..4dfeffd 100644
--- a/app/src/main/java/org/connectbot/HostListActivity.java
+++ b/app/src/main/java/org/connectbot/HostListActivity.java
@@ -21,6 +21,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -429,7 +430,8 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
}
- private class HostAdapter extends RecyclerView.Adapter<HostAdapter.ViewHolder> {
+ @VisibleForTesting
+ protected class HostAdapter extends RecyclerView.Adapter<HostAdapter.ViewHolder> {
private final LayoutInflater inflater;
private final List<HostBean> hosts;
private final TerminalManager manager;