package com.kickstarter.viewmodels;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.kickstarter.KSRobolectricTestCase;
import com.kickstarter.factories.ApiExceptionFactory;
import com.kickstarter.factories.ProjectFactory;
import com.kickstarter.factories.UpdateFactory;
import com.kickstarter.factories.UserFactory;
import com.kickstarter.libs.CurrentUserType;
import com.kickstarter.libs.Environment;
import com.kickstarter.libs.KoalaEvent;
import com.kickstarter.libs.MockCurrentUser;
import com.kickstarter.models.Comment;
import com.kickstarter.models.Project;
import com.kickstarter.models.Update;
import com.kickstarter.models.User;
import com.kickstarter.services.ApiClientType;
import com.kickstarter.services.MockApiClient;
import com.kickstarter.services.apiresponses.CommentsEnvelope;
import com.kickstarter.ui.IntentKey;
import com.kickstarter.ui.adapters.data.CommentsData;
import org.junit.Test;
import java.util.List;
import rx.Observable;
import rx.observers.TestSubscriber;
public class CommentsViewModelTest extends KSRobolectricTestCase {
@Test
public void testCommentsViewModel_EmptyState() {
final ApiClientType apiClient = new MockApiClient() {
@Override
public @NonNull Observable<CommentsEnvelope> fetchComments(final @NonNull Update update) {
return Observable.empty();
}
};
final Environment env = environment().toBuilder().apiClient(apiClient).build();
final CommentsViewModel vm = new CommentsViewModel(env);
final TestSubscriber<CommentsData> commentsData = new TestSubscriber<>();
vm.outputs.commentsData().subscribe(commentsData);
// Start the view model with an update.
vm.intent(new Intent().putExtra(IntentKey.UPDATE, UpdateFactory.update()));
// Only Viewed Comments event should fire.
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS);
commentsData.assertNoValues();
}
@Test
public void testCommentsViewModel_LoadMoreProjectComments() {
final CommentsViewModel vm = new CommentsViewModel(environment());
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, ProjectFactory.project()));
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS, KoalaEvent.PROJECT_COMMENT_VIEW);
// Paginate for older comments.
vm.inputs.nextPage();
// Modern and deprecated events emit for loading older project comments.
koalaTest.assertValues(
KoalaEvent.VIEWED_COMMENTS, KoalaEvent.PROJECT_COMMENT_VIEW, KoalaEvent.LOADED_OLDER_COMMENTS,
KoalaEvent.PROJECT_COMMENT_LOAD_OLDER
);
}
@Test
public void testCommentsViewModel_LoadMoreUpdateComments() {
final CommentsViewModel vm = new CommentsViewModel(environment());
// Start the view model with an update.
vm.intent(new Intent().putExtra(IntentKey.UPDATE, UpdateFactory.update()));
// Load older comments event should not fire yet.
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS);
// Paginate for older comments.
vm.inputs.nextPage();
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS, KoalaEvent.LOADED_OLDER_COMMENTS);
}
@Test
public void testCommentsViewModel_ProjectCommentsEmit() {
final CommentsViewModel vm = new CommentsViewModel(environment());
final TestSubscriber<List<Comment>> comments = new TestSubscriber<>();
vm.outputs.commentsData().map(CommentsData::comments).subscribe(comments);
final TestSubscriber<Boolean> isFetchingComments = new TestSubscriber<>();
vm.outputs.isFetchingComments().subscribe(isFetchingComments);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, ProjectFactory.project()));
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS, KoalaEvent.PROJECT_COMMENT_VIEW);
// Comments should emit.
comments.assertValueCount(1);
isFetchingComments.assertValues(true, false);
}
@Test
public void testCommentsViewModel_postCommentError() {
final ApiClientType apiClient = new MockApiClient() {
@Override
public @NonNull Observable<Comment> postComment(final @NonNull Project project, final @NonNull String body) {
return Observable.error(ApiExceptionFactory.badRequestException());
}
};
final Environment env = environment().toBuilder().apiClient(apiClient).build();
final CommentsViewModel vm = new CommentsViewModel(env);
final TestSubscriber<String> showPostCommentErrorToast = new TestSubscriber<>();
vm.outputs.showPostCommentErrorToast().subscribe(showPostCommentErrorToast);
final TestSubscriber<Void> showCommentPostedToast = new TestSubscriber<>();
vm.outputs.showCommentPostedToast().subscribe(showCommentPostedToast);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, ProjectFactory.backedProject()));
// Click the comment button and write a comment.
vm.inputs.commentButtonClicked();
vm.inputs.commentBodyChanged("Mic check mic check.");
// Post comment. Error should be shown. Comment posted toast should not be shown.
vm.inputs.postCommentClicked();
showPostCommentErrorToast.assertValueCount(1);
showCommentPostedToast.assertNoValues();
}
@Test
public void testCommentsViewModel_postCommentFlow() {
final CommentsViewModel vm = new CommentsViewModel(
environment().toBuilder().currentUser(new MockCurrentUser(UserFactory.user())).build()
);
final Project project = ProjectFactory.backedProject();
final TestSubscriber<Void> showCommentPostedToastTest = new TestSubscriber<>();
vm.outputs.showCommentPostedToast().subscribe(showCommentPostedToastTest);
final TestSubscriber<Void> dismissCommentDialogTest = new TestSubscriber<>();
vm.outputs.dismissCommentDialog().subscribe(dismissCommentDialogTest);
final TestSubscriber<Boolean> postButtonIsEnabledTest = new TestSubscriber<>();
vm.outputs.enablePostButton().subscribe(postButtonIsEnabledTest);
final TestSubscriber<Boolean> commentButtonHidden = new TestSubscriber<>();
vm.outputs.commentButtonHidden().subscribe(commentButtonHidden);
final TestSubscriber<Pair<Project, Boolean>> showCommentDialogTest = new TestSubscriber<>();
vm.outputs.showCommentDialog().subscribe(showCommentDialogTest);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS, KoalaEvent.PROJECT_COMMENT_VIEW);
// Comment button should be shown.
commentButtonHidden.assertValue(false);
// Click comment button. Comment dialog should be shown.
vm.inputs.commentButtonClicked();
showCommentDialogTest.assertValue(Pair.create(project, true));
// Write a comment. The post button should be enabled with valid comment body.
vm.inputs.commentBodyChanged("");
postButtonIsEnabledTest.assertValues(false);
vm.inputs.commentBodyChanged("Some comment");
postButtonIsEnabledTest.assertValues(false, true);
// Post comment. Dialog should be dismissed.
vm.inputs.postCommentClicked();
dismissCommentDialogTest.assertValueCount(1);
// Comment posted toast should be shown.
showCommentPostedToastTest.assertValueCount(1);
// A koala event for commenting should be tracked.
koalaTest.assertValues(
KoalaEvent.VIEWED_COMMENTS, KoalaEvent.PROJECT_COMMENT_VIEW, KoalaEvent.POSTED_COMMENT,
KoalaEvent.PROJECT_COMMENT_CREATE
);
}
@Test
public void testCommentsViewModel_loggedOutShowDialogFlow() {
final CurrentUserType currentUser = new MockCurrentUser(UserFactory.user());
final CommentsViewModel vm = new CommentsViewModel(environment().toBuilder().currentUser(currentUser).build());
final Project project = ProjectFactory.backedProject();
final TestSubscriber<Pair<Project, Boolean>> showCommentDialogTest = new TestSubscriber<>();
vm.outputs.showCommentDialog().subscribe(showCommentDialogTest);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
// The comment dialog should be hidden from logged out user.
showCommentDialogTest.assertNoValues();
// Login.
currentUser.refresh(UserFactory.user());
vm.inputs.loginSuccess();
// The comment dialog should be shown to backer.
showCommentDialogTest.assertValue(Pair.create(project, true));
}
@Test
public void testCommentsViewModel_showCommentButton_isBacking() {
final CommentsViewModel vm = new CommentsViewModel(
environment().toBuilder().currentUser(new MockCurrentUser(UserFactory.user())).build()
);
final TestSubscriber<Boolean> commentButtonHidden = new TestSubscriber<>();
vm.outputs.commentButtonHidden().subscribe(commentButtonHidden);
// Start the view model with a backed project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, ProjectFactory.backedProject()));
// The comment button should be shown to backer.
commentButtonHidden.assertValue(false);
}
@Test
public void testCommentsViewModel_commentButtonShown_isCreator() {
final User currentUser = UserFactory.user().toBuilder().id(1234).build();
final Project project = ProjectFactory.project()
.toBuilder()
.creator(currentUser)
.isBacking(false)
.build();
final CommentsViewModel vm = new CommentsViewModel(
environment().toBuilder().currentUser(new MockCurrentUser(currentUser)).build()
);
final TestSubscriber<Boolean> commentButtonHidden = new TestSubscriber<>();
vm.outputs.commentButtonHidden().subscribe(commentButtonHidden);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
// Comment button is shown for the creator.
commentButtonHidden.assertValues(false);
}
@Test
public void testCommentsViewModel_commentButtonHidden_notBackingNotCreator() {
final User creator = UserFactory.creator().toBuilder().id(222).build();
final User currentUser = UserFactory.user().toBuilder().id(111).build();
final Project project = ProjectFactory.project()
.toBuilder()
.creator(creator)
.isBacking(false)
.build();
final CommentsViewModel vm = new CommentsViewModel(
environment().toBuilder().currentUser(new MockCurrentUser(currentUser)).build()
);
final TestSubscriber<Boolean> commentButtonHidden = new TestSubscriber<>();
vm.outputs.commentButtonHidden().subscribe(commentButtonHidden);
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
// Comment button should be hidden if not backing and not creator.
commentButtonHidden.assertValue(true);
}
@Test
public void testCommentsViewModel_dismissCommentDialog() {
final CommentsViewModel vm = new CommentsViewModel(environment());
final TestSubscriber<Void> dismissCommentDialogTest = new TestSubscriber<>();
vm.outputs.dismissCommentDialog().subscribe(dismissCommentDialogTest);
final TestSubscriber<Pair<Project, Boolean>> showCommentDialogTest = new TestSubscriber<>();
vm.outputs.showCommentDialog().subscribe(showCommentDialogTest);
final Project project = ProjectFactory.backedProject();
// Start the view model with a project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
// The comment dialog should not be shown.
showCommentDialogTest.assertNoValues();
dismissCommentDialogTest.assertNoValues();
// Dismiss the comment dialog.
vm.inputs.commentDialogDismissed();
// The comment dialog should be dismissed.
dismissCommentDialogTest.assertValueCount(1);
showCommentDialogTest.assertValue(null);
}
@Test
public void testCommentsViewModel_currentCommentBody() {
final CommentsViewModel vm = new CommentsViewModel(environment());
final TestSubscriber<String> currentCommentBodyTest = new TestSubscriber<>();
vm.outputs.currentCommentBody().subscribe(currentCommentBodyTest);
currentCommentBodyTest.assertNoValues();
vm.inputs.commentBodyChanged("Hello");
currentCommentBodyTest.assertValues("Hello");
}
@Test
public void testCommentsViewModel_showCommentDialog() {
final CommentsViewModel vm = new CommentsViewModel(environment());
final TestSubscriber<Pair<Project, Boolean>> showCommentDialogTest = new TestSubscriber<>();
vm.outputs.showCommentDialog().subscribe(showCommentDialogTest);
final Project project = ProjectFactory.backedProject();
// Start the view model with a backed project.
vm.intent(new Intent().putExtra(IntentKey.PROJECT, project));
showCommentDialogTest.assertNoValues();
// Click the comment button.
vm.inputs.commentButtonClicked();
// The comment dialog should be shown.
showCommentDialogTest.assertValue(Pair.create(project, true));
}
@Test
public void testCommentsViewModel_UpdateCommentsEmit() {
final CommentsViewModel vm = new CommentsViewModel(environment());
final TestSubscriber<CommentsData> commentsData = new TestSubscriber<>();
vm.outputs.commentsData().subscribe(commentsData);
// Start the view model with an update.
vm.intent(new Intent().putExtra(IntentKey.UPDATE, UpdateFactory.update()));
koalaTest.assertValues(KoalaEvent.VIEWED_COMMENTS);
// Comments should emit.
commentsData.assertValueCount(1);
}
}