package com.novoda.bonfire.user; import android.support.test.InstrumentationRegistry; import android.support.test.espresso.contrib.RecyclerViewActions; import android.support.test.espresso.matcher.BoundedMatcher; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.support.v7.widget.RecyclerView; import android.view.View; import com.novoda.bonfire.R; import com.novoda.bonfire.TestDependencies; import com.novoda.bonfire.channel.data.model.Channel; import com.novoda.bonfire.channel.data.model.Channel.Access; import com.novoda.bonfire.channel.service.ChannelService; import com.novoda.bonfire.database.DatabaseResult; import com.novoda.bonfire.user.data.model.User; import com.novoda.bonfire.user.data.model.Users; import com.novoda.bonfire.user.service.UserService; import java.util.ArrayList; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import rx.Observable; import rx.android.schedulers.AndroidSchedulers; import rx.subjects.PublishSubject; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.isSelected; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static org.hamcrest.CoreMatchers.not; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class UsersActivityTest { private static final User AUTHENTICATED_USER = new User("logged_user_id", "Authenticated User", "http://invalid.photo/url"); private static final ArrayList<User> ALL_USERS = new ArrayList<>(); static { for (int i = 0; i < 5; i++) { ALL_USERS.add(new User("test_user_" + i, "Test User " + i, "http://non.existent.url/" + i)); } ALL_USERS.add(AUTHENTICATED_USER); } private ArrayList<User> channelOwners; @Rule public final ActivityTestRule<UsersActivity> activity = new ActivityTestRule<>(UsersActivity.class, false, false); private ChannelService channelService; private PublishSubject<DatabaseResult<Users>> subject; @Before public void setUp() throws Exception { channelService = Mockito.mock(ChannelService.class); UserService userService = Mockito.mock(UserService.class); when(userService.getAllUsers()).thenReturn(Observable.just(new Users(ALL_USERS))); TestDependencies.init() .withChannelService(channelService) .withUserService(userService); channelOwners = new ArrayList<>(); channelOwners.add(AUTHENTICATED_USER); channelOwnersUpdateAfterEveryChange(); } @Test public void userIsAddedToOwnersWhenClicked() throws Exception { launchUsersActivity(); clickOnUserAtPosition(1); onView(withId(R.id.users_recycler_view)).check(matches(atPosition(1, isSelected()))); } @Test public void userIsRemovedFromOwnersIfHeWasOwner() throws Exception { channelOwners.add(ALL_USERS.get(2)); launchUsersActivity(); clickOnUserAtPosition(2); onView(withId(R.id.users_recycler_view)).check(matches(atPosition(2, not(isSelected())))); } private void launchUsersActivity() { activity.launchActivity(UsersActivity.createIntentFor(InstrumentationRegistry.getContext(), new Channel("test_name", Access.PRIVATE))); subject.onNext(new DatabaseResult<>(new Users(channelOwners))); } private void clickOnUserAtPosition(int position) { onView(withId(R.id.users_recycler_view)).perform(RecyclerViewActions.actionOnItemAtPosition(position, click())); } private Matcher<View> atPosition(final int position, final Matcher<View> itemMatcher) { return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) { @Override public void describeTo(Description description) { description.appendText("has item at position " + position + ": "); itemMatcher.describeTo(description); } @Override protected boolean matchesSafely(RecyclerView view) { RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position); return viewHolder != null && itemMatcher.matches(viewHolder.itemView); } }; } private void channelOwnersUpdateAfterEveryChange() { subject = PublishSubject.create(); doAnswer(new Answer<Observable<DatabaseResult<Users>>>() { @Override public Observable<DatabaseResult<Users>> answer(InvocationOnMock invocation) throws Throwable { return subject.asObservable().observeOn(AndroidSchedulers.mainThread()); } }).when(channelService).getOwnersOfChannel(any(Channel.class)); doAnswer(new Answer<Observable<DatabaseResult<User>>>() { @Override public Observable<DatabaseResult<User>> answer(InvocationOnMock invocation) throws Throwable { User user = (User) invocation.getArguments()[1]; channelOwners.add(user); subject.onNext(new DatabaseResult<>(new Users(channelOwners))); return Observable.just(new DatabaseResult<>(user)); } }).when(channelService).addOwnerToPrivateChannel(any(Channel.class), any(User.class)); doAnswer(new Answer<Observable<DatabaseResult<User>>>() { @Override public Observable<DatabaseResult<User>> answer(InvocationOnMock invocation) throws Throwable { User user = (User) invocation.getArguments()[1]; channelOwners.remove(user); subject.onNext(new DatabaseResult<>(new Users(channelOwners))); return Observable.just(new DatabaseResult<>(user)); } }).when(channelService).removeOwnerFromPrivateChannel(any(Channel.class), any(User.class)); } }