org.ohmage.auth.AuthenticatorActivityTest.java Source code

Java tutorial

Introduction

Here is the source code for org.ohmage.auth.AuthenticatorActivityTest.java

Source

/*
 * Copyright (C) 2013 ohmage
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohmage.auth;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.test.suitebuilder.annotation.LargeTest;

import com.google.android.gms.plus.PlusClient;

import org.apache.http.auth.AuthenticationException;
import org.mockito.ArgumentMatcher;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.ohmage.app.OhmageService;
import org.ohmage.app.OhmageService.CancelableCallback;
import org.ohmage.app.R;
import org.ohmage.auth.AuthUtil.GrantType;
import org.ohmage.models.AccessToken;
import org.ohmage.models.User;
import org.ohmage.test.dagger.InjectedActivityInstrumentationTestCase;
import org.ohmage.test.dagger.PlusClientFragmentTestModule;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.inject.Inject;

import retrofit.RetrofitError;
import retrofit.client.Header;
import retrofit.client.Response;

import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click;
import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.scrollTo;
import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.typeText;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.doesNotExist;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
import static com.google.android.apps.common.testing.ui.espresso.matcher.RootMatchers.withDecorView;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isClickable;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.ohmage.test.OhmageViewMatchers.hasError;

/**
 * Functional Tests for the {@link org.ohmage.auth.AuthenticatorActivity}
 */
@LargeTest
public class AuthenticatorActivityTest extends InjectedActivityInstrumentationTestCase<AuthenticatorActivity> {

    @Inject
    AuthHelper fakeAuthHelper;
    @Inject
    AccountManager fakeAccountManager;
    @Inject
    OhmageService fakeOhmageService;
    @Inject
    PlusClientFragment fakePlusClientFragment;

    private static final String fakeGoogleEmail = "fake@gmail.com";
    private static final String fakeGoogleToken = "google_token";
    private static final String fakeEmail = "fake@email.com";
    private static final String fakePassword = "password";
    private static final String fakeFullname = "Full Name";
    private static final User fakeUser = new User();

    {
        fakeUser.email = fakeEmail;
        fakeUser.fullName = fakeFullname;
    }

    private CancelableCallback<User> fakeCallback = new CancelableCallback<User>() {
        @Override
        public void success(User user, Response response) {

        }

        @Override
        public void failure(RetrofitError error) {

        }
    };

    public AuthenticatorActivityTest() {
        super(AuthenticatorActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        when(fakeAccountManager.getAccountsByType(AuthUtil.ACCOUNT_TYPE)).thenReturn(new Account[] {});
        getActivity();
    }

    public void testStartingUp_noAccountExists_authButtonsAreVisible() {
        onView(withId(R.id.sign_in_google_button)).check(matches(isDisplayed()));

        onView(withId(R.id.sign_in_email_button)).check(matches(isDisplayed())).check(matches(isClickable()));

        onView(withId(R.id.create_account_button)).check(matches(isDisplayed())).check(matches(isClickable()));
    }

    public void testClicking_signInOhmageButton_showsSignInFragment() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.sign_in_ohmage_frame)).check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_validAccount_getsAccessToken() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        verify(fakeOhmageService).getAccessToken(eq(fakeEmail), eq(fakePassword), any(CancelableCallback.class));
    }

    public void testSignInWithOhmage_invalidAccount_showsSignInWithOhmage() throws Exception {
        when(fakeOhmageService.getAccessToken(fakeEmail, fakePassword)).thenThrow(new AuthenticationException());
        final Response fakeResponse = new Response(401, "", new ArrayList<Header>() {
        }, null);
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                ((CancelableCallback) invocation.getArguments()[2])
                        .failure(RetrofitError.httpError("", fakeResponse, null, null));
                return null;
            }
        }).when(fakeOhmageService).getAccessToken(eq(fakeEmail), eq(fakePassword), any(CancelableCallback.class));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).check(matches(isDisplayed()));
        onView(withId(R.id.password)).check(matches(isDisplayed()));
        onView(withId(R.id.sign_in_email_button)).check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_serverError_showsSignInWithOhmage() throws Exception {
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                ((CancelableCallback) invocation.getArguments()[2])
                        .failure(RetrofitError.unexpectedError("url", new IOException()));
                return null;
            }
        }).when(fakeOhmageService).getAccessToken(eq(fakeEmail), eq(fakePassword), any(CancelableCallback.class));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).check(matches(isDisplayed()));
        onView(withId(R.id.password)).check(matches(isDisplayed()));
        onView(withId(R.id.sign_in_email_button)).check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_noNetwork_showsSignInWithOhmage() throws Exception {
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                ((CancelableCallback) invocation.getArguments()[2])
                        .failure(RetrofitError.networkError("url", new IOException()));
                return null;
            }
        }).when(fakeOhmageService).getAccessToken(eq(fakeEmail), eq(fakePassword), any(CancelableCallback.class));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).check(matches(isDisplayed()));
        onView(withId(R.id.password)).check(matches(isDisplayed()));
        onView(withId(R.id.sign_in_email_button)).check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_noEmail_doesNotPerformNetworkRequest() throws Exception {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        verify(fakeOhmageService, never()).getAccessToken(anyString(), anyString());
    }

    public void testSignInWithOhmage_noEmail_showsErrorMessage() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withText(R.string.error_field_required))
                .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
                .check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_noEmail_emailFieldHasError() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).check(matches(hasError(R.string.error_field_required)));
    }

    public void testSignInWithOhmage_noPassword_doesNotPerformNetworkRequest() throws Exception {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        verify(fakeOhmageService, never()).getAccessToken(anyString(), anyString());
    }

    public void testSignInWithOhmage_noPassword_showsErrorMessage() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withText(R.string.error_field_required))
                .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
                .check(matches(isDisplayed()));
    }

    public void testSignInWithOhmage_noPassword_passwordFieldHasError() {
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.sign_in_email_button)).perform(click());

        onView(withId(R.id.password)).check(matches(hasError(R.string.error_field_required)));
    }

    public void testClicking_createAccountButton_showsSignInFragment() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.sign_in_ohmage_frame)).check(matches(isDisplayed()));
    }

    public void testCreateAccount_validInputs_startsCreateAccountRequestForUser() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        verify(fakeOhmageService).createUser(eq(fakePassword), argThat(new ArgumentMatcher<User>() {
            @Override
            public boolean matches(Object argument) {
                User arg = (User) argument;
                return fakeUser.email.equals(arg.email) && fakeUser.fullName.equals(arg.fullName);
            }
        }), any(CancelableCallback.class));
    }

    public void testCreateAccount_invalidEmail_doesNotPerformNetworkRequest() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText("blah"));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        verify(fakeOhmageService, never()).createUser(any(GrantType.class), anyString(), any(User.class),
                any(CancelableCallback.class));
    }

    public void testCreateAccount_invalidEmail_showsErrorMessage() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText("blah"));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withText(R.string.error_invalid_email_address))
                .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
                .check(matches(isDisplayed()));
    }

    public void testCreateAccount_invalidEmail_emailFieldHasError() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText("blah"));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withId(R.id.email)).check(matches(hasError(R.string.error_invalid_email_address)));
    }

    public void testCreateAccount_noEmail_doesNotPerformNetworkRequest() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        verify(fakeOhmageService, never()).createUser(any(GrantType.class), anyString(), any(User.class),
                any(CancelableCallback.class));
    }

    public void testCreateAccount_noEmail_showsErrorMessage() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withText(R.string.error_field_required))
                .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
                .check(matches(isDisplayed()));
    }

    public void testCreateAccount_noEmail_emailFieldHasError() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withId(R.id.email)).check(matches(hasError(R.string.error_field_required)));
    }

    public void testCreateAccount_noPassword_doesNotPerformNetworkRequest() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        verify(fakeOhmageService, never()).createUser(anyString(), any(User.class), any(CancelableCallback.class));
    }

    public void testCreateAccount_noPassword_showsErrorMessage() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withText(R.string.error_field_required))
                .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
                .check(matches(isDisplayed()));
    }

    public void testCreateAccount_noPassword_passwordFieldHasError() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.fullname)).perform(scrollTo(), typeText(fakeFullname));
        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        onView(withId(R.id.password)).check(matches(hasError(R.string.error_field_required)));
    }

    public void testCreateAccount_noFullName_startsCreateAccountRequest() {
        onView(withId(R.id.create_account_button)).perform(click());
        final User fakeUser = new User();
        fakeUser.email = fakeEmail;

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        verify(fakeOhmageService).createUser(eq(fakePassword), argThat(new ArgumentMatcher<User>() {
            @Override
            public boolean matches(Object argument) {
                User arg = (User) argument;
                // Check that email and full name are the same as our fake user
                return fakeUser.email.equals(arg.email) && (fakeUser.fullName == null ? arg.fullName == null
                        : fakeUser.fullName.equals(arg.fullName));
            }
        }), any(CancelableCallback.class));
    }

    public void testCreateAccount_noFullName_fullNameFieldDoesNotHaveError() {
        onView(withId(R.id.create_account_button)).perform(click());

        onView(withId(R.id.email)).perform(scrollTo(), typeText(fakeEmail));
        onView(withId(R.id.password)).perform(scrollTo(), typeText(fakePassword));
        onView(withId(R.id.create_account_button)).perform(scrollTo(), click());

        // The view disappears once the create_account_button is clicked if there are no errors
        onView(withId(R.id.fullname)).check(doesNotExist());
    }

    public void testClicking_signInWithGoogleButton_startsConnectOnPlusClient() throws Exception {
        onView(withId(R.id.sign_in_google_button)).perform(click());

        verify(fakePlusClientFragment).signIn(anyInt());
    }

    public void testPlusClientConnects_successfully_triesToGetGoogleAuthToken() throws Exception {
        setPlusClientFragmentSuccessfullyConnects();

        onView(withId(R.id.sign_in_google_button)).perform(click());

        verify(fakeAuthHelper).googleAuthGetToken(fakeGoogleEmail);
    }

    public void testGoogleAuthToken_receivedSuccessfully_triesToAuthWithOhmage() throws Throwable {
        setPlusClientFragmentSuccessfullyConnects();
        when(fakeAuthHelper.googleAuthGetToken(fakeGoogleEmail)).thenReturn(fakeGoogleToken);

        onView(withId(R.id.sign_in_google_button)).perform(click());

        verify(fakeOhmageService).getAccessToken(eq(AuthUtil.GrantType.GOOGLE_OAUTH2), eq(fakeGoogleToken),
                any(CancelableCallback.class));
    }

    public void testAuthTokenReceived_validAccessToken_createsOhmageAccount() throws Throwable {
        final AccessToken fakeAccessToken = new AccessToken("token", "refresh", "user");
        setPlusClientFragmentSuccessfullyConnects();
        when(fakeAuthHelper.googleAuthGetToken(fakeGoogleEmail)).thenReturn(fakeGoogleToken);
        // For some reason the argument captor messes up the next test so I'm doing this
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                ((CancelableCallback) invocation.getArguments()[2]).success(fakeAccessToken, null);
                return null;
            }
        }).when(fakeOhmageService).getAccessToken(eq(AuthUtil.GrantType.GOOGLE_OAUTH2), eq(fakeGoogleToken),
                any(CancelableCallback.class));

        onView(withId(R.id.sign_in_google_button)).perform(click());

        Account account = new Account(fakeGoogleEmail, AuthUtil.ACCOUNT_TYPE);
        verify(fakeAccountManager).addAccountExplicitly(account, "refresh", null);
        verify(fakeAccountManager).setAuthToken(account, AuthUtil.AUTHTOKEN_TYPE, "token");
    }

    private void setPlusClientFragmentSuccessfullyConnects() {
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
                PlusClient fakePlusClient = mock(PlusClient.class);
                when(fakePlusClient.getAccountName()).thenReturn(fakeGoogleEmail);
                getActivity().onSignedIn(fakePlusClient);
                return null;
            }
        }).when(fakePlusClientFragment).signIn(anyInt());
    }

    @Override
    public List<Object> getModules() {
        return Arrays.<Object>asList(new PlusClientFragmentTestModule());
    }
}