package com.novoda.bonfire.chat; import android.support.test.InstrumentationRegistry; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; 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.chat.data.model.Chat; import com.novoda.bonfire.chat.data.model.Message; import com.novoda.bonfire.chat.service.ChatService; import com.novoda.bonfire.database.DatabaseResult; import com.novoda.bonfire.login.data.model.Authentication; import com.novoda.bonfire.login.service.LoginService; import com.novoda.bonfire.user.data.model.User; import java.util.ArrayList; 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.*; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.matcher.ViewMatchers.*; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.Matchers.equalToIgnoringCase; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class ChatActivityTest { private static final User AUTHENTICATED_USER = new User("user_id", "Test User", "http://invalid.url"); private static final ArrayList<Message> MESSAGES = new ArrayList<>(); private static final DatabaseResult<Chat> CHAT = new DatabaseResult<>(new Chat(MESSAGES)); private static final String CHANNEL_NAME = "test_channel"; @Rule public final ActivityTestRule<ChatActivity> activity = new ActivityTestRule<>(ChatActivity.class, false, false); private ChatService chatService; @Before public void setUp() throws Exception { chatService = Mockito.mock(ChatService.class); LoginService loginService = Mockito.mock(LoginService.class); when(loginService.getAuthentication()).thenReturn(Observable.just(new Authentication(AUTHENTICATED_USER))); TestDependencies.init() .withChatService(chatService) .withLoginService(loginService); } @Test public void channelNameIsShownInToolbar() throws Exception { givenChatIsEmpty(); launchChatActivityWithAccess(Access.PRIVATE); onView(allOf(isDescendantOfA(withId(R.id.toolbar)), withText(CHANNEL_NAME))).check(matches(isDisplayed())); } @Test public void cannotManageUsersInPublicChannel() throws Exception { givenChatIsEmpty(); launchChatActivityWithAccess(Access.PUBLIC); onView(withId(R.id.toolbar)).check(matches(not(hasDescendant(withId(R.id.manageOwners))))); } @Test public void canManageUsersWhenChannelIsPrivate() throws Exception { givenChatIsEmpty(); launchChatActivityWithAccess(Access.PRIVATE); onView(allOf(isDescendantOfA(withId(R.id.toolbar)), withId(R.id.manageOwners))).check(matches(isDisplayed())); } @Test public void submitButtonIsDisabledWhenMessageIsEmpty() throws Exception { givenChatIsEmpty(); launchChatActivityWithAccess(Access.PUBLIC); onView(withId(R.id.submit_button)).check(matches(not(isEnabled()))); onView(withId(R.id.message_edit)).perform(typeText("random message text"), closeSoftKeyboard(), clearText()); onView(withId(R.id.submit_button)).check(matches(not(isEnabled()))); } @Test public void submitButtonIsEnabledWhenThereIsAMessage() throws Exception { givenChatIsEmpty(); launchChatActivityWithAccess(Access.PRIVATE); onView(withId(R.id.message_edit)).perform(typeText("not so random message text"), closeSoftKeyboard()); onView(withId(R.id.submit_button)).check(matches(isEnabled())); } private void givenChatIsEmpty() { when(chatService.getChat(any(Channel.class))).thenReturn(Observable.just(CHAT)); } @Test public void submittedMessageShowsInTheChat() throws Exception { givenChatUpdatesAfterEveryMessage(); launchChatActivityWithAccess(Access.PRIVATE); String message = "test message"; submitMessage(message); assertThatMessageFromUserIsShownInChat(AUTHENTICATED_USER.getName(), message); } private void launchChatActivityWithAccess(Access access) { activity.launchActivity(ChatActivity.createIntentFor(InstrumentationRegistry.getContext(), new Channel(CHANNEL_NAME, access))); } private void givenChatUpdatesAfterEveryMessage() { final PublishSubject<DatabaseResult<Chat>> subject = PublishSubject.create(); doAnswer(new Answer<Observable<DatabaseResult<Chat>>>() { @Override public Observable<DatabaseResult<Chat>> answer(InvocationOnMock invocation) throws Throwable { return subject.asObservable() .observeOn(AndroidSchedulers.mainThread()) .startWith(CHAT); } }).when(chatService).getChat(any(Channel.class)); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { MESSAGES.add((Message) invocation.getArguments()[1]); subject.onNext(CHAT); return null; } }).when(chatService).sendMessage(any(Channel.class), any(Message.class)); } private void assertThatMessageFromUserIsShownInChat(String userName, String message) { onView(withId(R.id.message_author_image)).check(matches(isDisplayed())); onView(allOf(withId(R.id.message_body), withText(equalToIgnoringCase(message)))).check(matches(isDisplayed())); } private void submitMessage(String message) { onView(withId(R.id.message_edit)).perform(typeText(message), closeSoftKeyboard()); onView(withId(R.id.submit_button)).perform(click()); } }