diff --git a/.gitignore b/.gitignore
index f84281fbed..9279cd5b9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ signing.properties
library/lib/
library/obj/
ffpr
+test/androidTestEspresso/res/values/arrays.xml
diff --git a/build.gradle b/build.gradle
index 5e0d140351..1c426ea026 100644
--- a/build.gradle
+++ b/build.gradle
@@ -78,7 +78,9 @@ dependencies {
androidTestCompile ('com.squareup.assertj:assertj-android:1.0.0') {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
- androidTestCompile 'com.android.support.test:runner:0.2'
+ androidTestCompile ('com.android.support.test.espresso:espresso-core:2.1') {
+ exclude group: 'javax.inject'
+ }
}
dependencyVerification {
@@ -144,6 +146,13 @@ android {
buildConfigField "String", "PUSH_URL", "\"https://textsecure-service.whispersystems.org\""
}
+ productFlavors {
+ base { }
+ espresso {
+ testInstrumentationRunner "org.thoughtcrime.securesms.TextSecureWakingTestRunner"
+ }
+ }
+
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
@@ -201,9 +210,16 @@ android {
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}
+ espresso {
+ manifest.srcFile 'test/espresso/AndroidManifest.xml'
+ }
androidTest {
java.srcDirs = ['test/androidTest/java']
}
+ androidTestEspresso {
+ java.srcDirs = ['test/androidTestEspresso/java']
+ res.srcDirs = ['test/androidTestEspresso/res']
+ }
}
lintOptions {
diff --git a/proguard-testing.pro b/proguard-testing.pro
index affdfd8b0e..9932571902 100644
--- a/proguard-testing.pro
+++ b/proguard-testing.pro
@@ -4,6 +4,7 @@
-dontwarn android.test.**
-dontwarn com.android.support.test.**
-dontwarn sun.reflect.**
+-dontwarn sun.misc.**
-dontwarn org.assertj.**
-dontwarn org.hamcrest.**
-dontwarn org.mockito.**
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityActions.java
new file mode 100644
index 0000000000..4794bd6d72
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityActions.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class ApplicationPreferencesActivityActions {
+
+ public static void clickSmsAndMmsSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_sms_mms"))).perform(click());
+ }
+
+ public static void clickNotificationsSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_notifications"))).perform(click());
+ }
+
+ public static void clickAppProtectionSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_app_protection"))).perform(click());
+ }
+
+ public static void clickAppearanceSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_appearance"))).perform(click());
+ }
+
+ public static void clickDeleteOldMessagesSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_storage"))).perform(click());
+ }
+
+ public static void clickAdvancedSetting() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_advanced"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityTest.java
new file mode 100644
index 0000000000..ef4ccf1497
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ApplicationPreferencesActivityTest.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static org.thoughtcrime.securesms.EspressoUtil.waitOn;
+
+@LargeTest
+public class ApplicationPreferencesActivityTest extends TextSecureEspressoTestCase {
+
+ public ApplicationPreferencesActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkAllPreferencesDisplayed() throws Exception {
+ onData(Matchers.allOf(withKey("preference_category_sms_mms")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("preference_category_notifications")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("preference_category_app_protection")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("preference_category_appearance")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("preference_category_storage")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("preference_category_advanced")))
+ .check(matches(isDisplayed()));
+ }
+
+ public void testClickSettings() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ checkAllPreferencesDisplayed();
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationActivityActions.java
new file mode 100644
index 0000000000..b0be6884f5
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationActivityActions.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.content.Context;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.thoughtcrime.securesms.EspressoUtil.typeTextAndCloseKeyboard;
+
+public class ConversationActivityActions {
+
+ public static void clickAddAttachment(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.conversation__menu_add_attachment)).perform(click());
+ }
+
+ public static void clickAllImages(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.conversation__menu_view_media)).perform(click());
+ }
+
+ public static void clickDeleteThread(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.conversation__menu_delete_thread)).perform(click());
+ }
+
+ public static void toggleEmojiKeyboard() throws Exception {
+ onView(withId(R.id.emoji_toggle)).perform(click());
+ }
+
+ public static void typeMessage(String message) throws Exception {
+ typeTextAndCloseKeyboard(onView(withId(R.id.embedded_text_editor)), message);
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityActions.java
new file mode 100644
index 0000000000..915a0f2704
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityActions.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.content.Context;
+
+import org.thoughtcrime.securesms.R;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+public class ConversationListActivityActions {
+
+ public static void dismissReminder() throws Exception {
+ onView(withId(R.id.cancel)).perform(click());
+ }
+
+ public static void clickNewConversation() throws Exception {
+ onView(withId(R.id.fab)).perform(click());
+ }
+
+ public static void clickNewGroup(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.text_secure_normal__menu_new_group)).perform(click());
+ }
+
+ public static void clickLock(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.text_secure_normal__menu_clear_passphrase)).perform(click());
+ }
+
+ public static void clickMarkAllRead(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.text_secure_normal__mark_all_as_read)).perform(click());
+ }
+
+ public static void clickImportExport(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.arrays__import_export)).perform(click());
+ }
+
+ public static void clickMyIdentity(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.arrays__my_identity_key)).perform(click());
+ }
+
+ public static void clickSettings(Context context) throws Exception {
+ openActionBarOverflowOrOptionsMenu(context);
+ onView(withText(R.string.text_secure_normal__menu_settings)).perform(click());
+ }
+
+ public static void deleteSelected() throws Exception {
+ onView(withId(R.id.menu_delete_selected)).perform(click());
+ onView(withText(R.string.delete)).perform(click());
+ }
+
+ public static void cancelDeleteSelected() throws Exception {
+ onView(withId(R.id.menu_delete_selected)).perform(click());
+ onView(withText(android.R.string.cancel)).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java
new file mode 100644
index 0000000000..292f3e2051
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java
@@ -0,0 +1,279 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.widget.TextView;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.thoughtcrime.securesms.components.DefaultSmsReminder;
+import org.thoughtcrime.securesms.components.ExpiredBuildReminder;
+import org.thoughtcrime.securesms.components.PushRegistrationReminder;
+import org.thoughtcrime.securesms.components.SystemSmsImportReminder;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.longClick;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+import static org.thoughtcrime.securesms.EspressoUtil.addContact;
+import static org.thoughtcrime.securesms.EspressoUtil.waitOn;
+import static org.thoughtcrime.securesms.ViewMatchers.withRecyclerItem;
+import static org.thoughtcrime.securesms.ViewMatchers.withRecyclerItemCount;
+
+@LargeTest
+public class ConversationListActivityTest extends TextSecureEspressoTestCase {
+ private final static String TAG = ConversationListActivityTest.class.getSimpleName();
+
+ public ConversationListActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkOptionsMenuItemsDisplayed() throws Exception {
+ onView(withContentDescription(getContext().getString(R.string.conversation_list__menu_search)))
+ .check(matches(isDisplayed()));
+
+ openActionBarOverflowOrOptionsMenu(getContext());
+ onView(withText(R.string.text_secure_normal__menu_new_group)).check(matches(isDisplayed()));
+ onView(withText(R.string.text_secure_normal__mark_all_as_read)).check(matches(isDisplayed()));
+ onView(withText(R.string.arrays__import_export)).check(matches(isDisplayed()));
+ onView(withText(R.string.arrays__my_identity_key)).check(matches(isDisplayed()));
+ onView(withText(R.string.text_secure_normal__menu_settings)).check(matches(isDisplayed()));
+
+ if (!TextSecurePreferences.isPasswordDisabled(getContext())) {
+ onView(withText(R.string.text_secure_normal__menu_clear_passphrase))
+ .check(matches(isDisplayed()));
+ }
+
+ pressBack();
+ }
+
+ private boolean checkReminderIsDisplayed() throws Exception {
+ boolean reminderVisible = true;
+ Integer reminderTitleResId = null;
+ Integer reminderTextResId = null;
+
+ if (ExpiredBuildReminder.isEligible(getContext())) {
+ reminderTitleResId = R.string.reminder_header_expired_build;
+ reminderTextResId = R.string.reminder_header_expired_build_details;
+ } else if (DefaultSmsReminder.isEligible(getContext())) {
+ reminderTitleResId = R.string.reminder_header_sms_default_title;
+ reminderTextResId = R.string.reminder_header_sms_default_text;
+ } else if (SystemSmsImportReminder.isEligible(getContext())) {
+ reminderTitleResId = R.string.reminder_header_sms_import_title;
+ reminderTextResId = R.string.reminder_header_sms_import_text;
+ } else if (PushRegistrationReminder.isEligible(getContext())) {
+ reminderTitleResId = R.string.reminder_header_push_title;
+ reminderTextResId = R.string.reminder_header_push_text;
+ } else {
+ reminderVisible = false;
+ }
+
+ if (reminderVisible) {
+ onView(withId(R.id.reminder_title)).check(matches(isDisplayed()));
+ onView(withId(R.id.reminder_title)).check(matches(withText(reminderTitleResId)));
+ onView(withId(R.id.reminder_text)).check(matches(isDisplayed()));
+ onView(withId(R.id.reminder_text)).check(matches(withText(reminderTextResId)));
+ }
+
+ return reminderVisible;
+ }
+
+ private void loadAndCheckState() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ checkOptionsMenuItemsDisplayed();
+ checkReminderIsDisplayed();
+ }
+
+ public void testDismissAllReminders() throws Exception {
+ loadAndCheckState();
+
+ int expectedReminders = 0;
+ if (ExpiredBuildReminder.isEligible(getContext())) expectedReminders++;
+ if (DefaultSmsReminder.isEligible(getContext())) expectedReminders++;
+ if (SystemSmsImportReminder.isEligible(getContext())) expectedReminders++;
+
+ Log.d(TAG, "expecting to see " + expectedReminders + " reminders");
+ while (expectedReminders > 0) {
+ if (!checkReminderIsDisplayed()) {
+ throw new IllegalStateException("expected to see " + expectedReminders + " more reminders");
+ }
+
+ Log.d(TAG, "found reminder, dismissing now");
+ ConversationListActivityActions.dismissReminder();
+ expectedReminders--;
+
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ pressBack();
+ waitOn(ConversationListActivity.class);
+ }
+
+ if (checkReminderIsDisplayed() && !PushRegistrationReminder.isEligible(getContext())) {
+ throw new IllegalStateException("only expected to see " + expectedReminders + " reminders");
+ }
+ }
+
+ public void testClickNewConversation() throws Exception {
+ loadAndCheckState();
+ ConversationListActivityActions.clickNewConversation();
+ waitOn(NewConversationActivity.class);
+ }
+
+ public void testClickNewGroup() throws Exception {
+ loadAndCheckState();
+ ConversationListActivityActions.clickNewGroup(getContext());
+ waitOn(GroupCreateActivity.class);
+ }
+
+ public void testClickImportExport() throws Exception {
+ loadAndCheckState();
+ ConversationListActivityActions.clickImportExport(getContext());
+ waitOn(ImportExportActivity.class);
+ }
+
+ public void testClickMyIdentity() throws Exception {
+ loadAndCheckState();
+ ConversationListActivityActions.clickMyIdentity(getContext());
+ waitOn(ViewLocalIdentityActivity.class);
+ }
+
+ public void testClickSettings() throws Exception {
+ loadAndCheckState();
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ }
+
+ public static Matcher isThreadFrom(final String contactName) {
+ return new TypeSafeMatcher() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is thread from: " + contactName);
+ }
+
+ @Override
+ public boolean matchesSafely(Object object) {
+ if (!(object instanceof ConversationListItem)) {
+ return false;
+ }
+
+ ConversationListItem itemView = (ConversationListItem) object;
+ TextView fromView = (TextView) itemView.findViewById(R.id.from);
+ return fromView.getText().toString().equals(contactName);
+ }
+ };
+ }
+
+ public static Matcher withThreadSnippet(final String snippet) {
+ return new TypeSafeMatcher() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is thread with snippet: " + snippet);
+ }
+
+ @Override
+ public boolean matchesSafely(Object object) {
+ if (!(object instanceof ConversationListItem)) {
+ return false;
+ }
+
+ ConversationListItem itemView = (ConversationListItem) object;
+ TextView snippetView = (TextView) itemView.findViewById(R.id.subject);
+ return snippetView.getText().toString().equals(snippet);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testSaveTextDraft() throws Exception {
+ final String CONTACT_NAME = "Clement Duval";
+ final String CONTACT_NUMBER = "55555555555";
+ final String DRAFT_MESSAGE = "I struck him in the name of liberty";
+ final String DRAFT_SNIPPET = getContext().getString(R.string.ThreadRecord_draft) + " " + DRAFT_MESSAGE;
+
+ addContact(getContext(), CONTACT_NAME, CONTACT_NUMBER);
+ loadAndCheckState();
+
+ ConversationListActivityActions.clickNewConversation();
+ waitOn(NewConversationActivity.class);
+ NewConversationActivityActions.clickContactWithName(CONTACT_NAME);
+ waitOn(ConversationActivity.class);
+ ConversationActivityActions.typeMessage(DRAFT_MESSAGE);
+ pressBack();
+ waitOn(ConversationListActivity.class);
+
+ onView(withId(R.id.list)).check(matches(
+ withRecyclerItemCount(1L)
+ ));
+ onView(withId(R.id.list)).check(matches(
+ withRecyclerItem(allOf(
+ isDisplayed(),
+ isThreadFrom(CONTACT_NAME),
+ withThreadSnippet(DRAFT_SNIPPET)))
+ ));
+ }
+
+ /*
+ this is known to fail on some older devices due to some espresso
+ related app-compat bug
+ */
+ @SuppressWarnings("unchecked")
+ public void testSaveDeleteTextDraft() throws Exception {
+ final String CONTACT_NAME = "Clement Duval";
+ final String CONTACT_NUMBER = "55555555555";
+ final String DRAFT_MESSAGE = "I struck him in the name of liberty";
+ final String DRAFT_SNIPPET = getContext().getString(R.string.ThreadRecord_draft) + " " + DRAFT_MESSAGE;
+
+ addContact(getContext(), CONTACT_NAME, CONTACT_NUMBER);
+ loadAndCheckState();
+
+ ConversationListActivityActions.clickNewConversation();
+ waitOn(NewConversationActivity.class);
+ NewConversationActivityActions.clickContactWithName(CONTACT_NAME);
+ waitOn(ConversationActivity.class);
+ ConversationActivityActions.typeMessage(DRAFT_MESSAGE);
+ pressBack();
+ waitOn(ConversationListActivity.class);
+
+ onView(withId(R.id.list)).check(matches(
+ withRecyclerItemCount(1L)
+ ));
+ onView(withId(R.id.list)).check(matches(
+ withRecyclerItem(allOf(
+ isDisplayed(),
+ isThreadFrom(CONTACT_NAME),
+ withThreadSnippet(DRAFT_SNIPPET)))
+ ));
+
+ onView(withText(CONTACT_NAME)).perform(longClick());
+ ConversationListActivityActions.deleteSelected();
+
+ onView(withId(R.id.list)).check(matches(
+ withRecyclerItemCount(0L)
+ ));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/EspressoUtil.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/EspressoUtil.java
new file mode 100644
index 0000000000..5fac6acdf2
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/EspressoUtil.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityMonitor;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.support.test.espresso.NoActivityResumedException;
+import android.support.test.espresso.ViewInteraction;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import static android.support.test.espresso.action.ViewActions.replaceText;
+import static android.support.test.espresso.action.ViewActions.typeText;
+import static android.support.test.espresso.Espresso.closeSoftKeyboard;
+import static android.support.test.espresso.Espresso.pressBack;
+
+public class EspressoUtil {
+ private final static String TAG = EspressoUtil.class.getSimpleName();
+
+ public static void waitOn(Class extends Activity> clazz) {
+ Log.w(TAG, "waiting for " + clazz.getName());
+ new ActivityMonitor(clazz.getName(), null, true).waitForActivityWithTimeout(10000);
+ }
+
+ public static void actuallyCloseSoftKeyboard() throws Exception {
+ closeSoftKeyboard();
+ Thread.sleep(800);
+ }
+
+ public static void typeTextAndCloseKeyboard(ViewInteraction view, String text) throws Exception {
+ view.perform(typeText(text));
+ actuallyCloseSoftKeyboard();
+ }
+
+ public static void replaceTextAndCloseKeyboard(ViewInteraction view, String text) throws Exception {
+ view.perform(replaceText(text));
+ actuallyCloseSoftKeyboard();
+ }
+
+ public static void closeAllActivities() throws Exception {
+ for (int i = 0; i < 10; i++) {
+ try {
+
+ pressBack();
+
+ } catch (NoActivityResumedException e) {
+ Log.d(TAG, "you made me do this, android");
+ return;
+ }
+ }
+
+ throw new IllegalStateException("what are you doing with 10 open activities?!");
+ }
+
+ public static long addContact(Context context, String name, String number) throws Exception {
+ ArrayList operations = new ArrayList<>();
+
+ operations.add(ContentProviderOperation
+ .newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null)
+ .build());
+
+ operations.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
+ .build());
+
+ operations.add(ContentProviderOperation.
+ newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, number)
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
+ .build());
+
+ return context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations).length;
+ }
+
+ public static void removeAllContacts(Context context) throws Exception {
+ ContentResolver contentResolver = context.getContentResolver();
+ Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
+
+ try {
+
+ while (cursor.moveToNext()) {
+ String contactKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
+ Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, contactKey);
+ contentResolver.delete(contactUri, null, null);
+ }
+
+ } finally {
+ cursor.close();
+ }
+ }
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/GroupCreateActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/GroupCreateActivityTest.java
new file mode 100644
index 0000000000..b9656c8136
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/GroupCreateActivityTest.java
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.not;
+
+@LargeTest
+public class GroupCreateActivityTest extends TextSecureEspressoTestCase {
+
+ public GroupCreateActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void clickNewGroup() throws Exception {
+ ConversationListActivityActions.clickNewGroup(getContext());
+ EspressoUtil.waitOn(GroupCreateActivity.class);
+ }
+
+ public void testLayoutWithPush() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewGroup();
+
+ onView(ViewMatchers.withId(R.id.push_disabled)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.push_disabled_reason)).check(matches(not(isDisplayed())));
+ }
+
+ public void testLayoutWithoutPush() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ clickNewGroup();
+
+ onView(ViewMatchers.withId(R.id.push_disabled)).check(matches(isDisplayed()));
+ onView(withId(R.id.push_disabled_reason)).check(matches(isDisplayed()));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ImportExportActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ImportExportActivityTest.java
new file mode 100644
index 0000000000..e0f63b8ac8
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ImportExportActivityTest.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.swipeLeft;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+@LargeTest
+public class ImportExportActivityTest extends TextSecureEspressoTestCase {
+
+ public ImportExportActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkImportLayout() throws Exception {
+ onView(ViewMatchers.withId(R.id.import_sms)).check(matches(isDisplayed()));
+ onView(withId(R.id.import_encrypted_backup)).check(matches(isDisplayed()));
+ onView(withId(R.id.import_plaintext_backup)).check(matches(isDisplayed()));
+ }
+
+ private void checkExportLayout() throws Exception {
+ onView(withId(R.id.export_plaintext_backup)).check(matches(isDisplayed()));
+ }
+
+ private void clickImportExport() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickImportExport(getContext());
+ EspressoUtil.waitOn(ImportExportActivity.class);
+ }
+
+ public void testLayout() throws Exception {
+ clickImportExport();
+ checkImportLayout();
+ onView(withId(R.id.import_sms)).perform(swipeLeft());
+ checkExportLayout();
+ onView(withId(R.id.export_plaintext_backup)).perform(swipeRight());
+ checkImportLayout();
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityActions.java
new file mode 100644
index 0000000000..e9fc2ba935
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityActions.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsString;
+
+public class NewConversationActivityActions {
+
+ public static void filterNameOrNumber(String nameOrNumber) throws Exception {
+ EspressoUtil.replaceTextAndCloseKeyboard(onView(withId(R.id.filter)), nameOrNumber);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void clickContactWithName(String name) throws Exception {
+ onView(allOf(withId(R.id.name), withText(name))).perform(click());
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void clickContactWithNumber(String number) throws Exception {
+ onView(allOf(withId(R.id.number), withText(containsString(number)))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityTest.java
new file mode 100644
index 0000000000..3f4f9b81af
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/NewConversationActivityTest.java
@@ -0,0 +1,125 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+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.hamcrest.Matchers.allOf;
+
+@LargeTest
+public class NewConversationActivityTest extends TextSecureEspressoTestCase {
+
+ final String[][] TEST_CONTACTS = {
+ {"Nadezhda Tolokonnikova", "11111111111", "Nad", "ezh", "ova"},
+ {"Jules Bonnot", "22222222222", "Jul", "nno", "not"},
+ {"Masha Kolenkia", "33333333333", "Mas", "len", "kia"},
+ {"Chairman Meow", "44444444444", "Cha", "rma", "eow"},
+ {"Clement Duval", "55555555555", "Cle", "eme", "val"},
+ {"Nestor Makhno", "66666666666", "Nes", "sto", "hno"},
+ {"Wilhelm Reich", "77777777777", "Wil", "hel", "ich"}
+ };
+
+ public NewConversationActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void populateContacts() throws Exception {
+ for(String[] TEST_CONTACT : TEST_CONTACTS) {
+ EspressoUtil.addContact(getContext(), TEST_CONTACT[0], TEST_CONTACT[1]);
+ }
+ }
+
+ private void clickNewConversation() throws Exception{
+ ConversationListActivityActions.clickNewConversation();
+ EspressoUtil.waitOn(NewConversationActivity.class);
+ EspressoUtil.actuallyCloseSoftKeyboard();
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testContactFilterPrefix() throws Exception {
+ populateContacts();
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewConversation();
+
+ for (String[] TEST_CONTACT : TEST_CONTACTS) {
+ NewConversationActivityActions.filterNameOrNumber(TEST_CONTACT[2]);
+ onView(allOf(withId(R.id.name), withText(TEST_CONTACT[0]))).check(matches(isDisplayed()));
+ }
+ }
+
+ // this is known to fail on some devices, see #2378
+ @SuppressWarnings("unchecked")
+ public void testContactFilterMiddle() throws Exception {
+ populateContacts();
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewConversation();
+
+ for (String[] TEST_CONTACT : TEST_CONTACTS) {
+ NewConversationActivityActions.filterNameOrNumber(TEST_CONTACT[3]);
+ onView(allOf(withId(R.id.name), withText(TEST_CONTACT[0]))).check(matches(isDisplayed()));
+ }
+ }
+
+ // this is known to fail on some devices, see #2378
+ @SuppressWarnings("unchecked")
+ public void testContactFilterPostfix() throws Exception {
+ populateContacts();
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewConversation();
+
+ for (String[] TEST_CONTACT : TEST_CONTACTS) {
+ NewConversationActivityActions.filterNameOrNumber(TEST_CONTACT[4]);
+ onView(allOf(withId(R.id.name), withText(TEST_CONTACT[0]))).check(matches(isDisplayed()));
+ }
+ }
+
+ public void testNewConversationWithNonPushContact() throws Exception {
+ populateContacts();
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewConversation();
+
+ NewConversationActivityActions.clickContactWithName("Chairman Meow");
+ EspressoUtil.waitOn(ConversationActivity.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testNewConversationWithUnknownWhenRegistered() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickNewConversation();
+
+ NewConversationActivityActions.filterNameOrNumber("8888888888");
+ NewConversationActivityActions.clickContactWithNumber("8888888888");
+ EspressoUtil.waitOn(ConversationActivity.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testNewConversationWithUnknownWhenUnregistered() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ clickNewConversation();
+
+ NewConversationActivityActions.filterNameOrNumber("8888888888");
+ NewConversationActivityActions.clickContactWithNumber("8888888888");
+ EspressoUtil.waitOn(ConversationActivity.class);
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityActions.java
new file mode 100644
index 0000000000..5d83d9533d
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityActions.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+public class PassphraseChangeActivityActions {
+
+ public static void clickOk() throws Exception {
+ onView(withId(R.id.ok_button)).perform(click());
+ }
+
+ public static void clickCancel() throws Exception {
+ onView(withId(R.id.cancel_button)).perform(click());
+ }
+
+ public static void typeNewPassphrase(String passphrase, String repeat) throws Exception {
+ EspressoUtil.typeTextAndCloseKeyboard(onView(withId(R.id.new_passphrase)), passphrase);
+ EspressoUtil.typeTextAndCloseKeyboard(onView(withId(R.id.repeat_passphrase)), repeat);
+ }
+
+ public static void typeNewPassphrase(String passphrase) throws Exception {
+ typeNewPassphrase(passphrase, passphrase);
+ }
+
+ public static void typeChangePassphrase(String oldPassphrase, String newPassphrase) throws Exception {
+ EspressoUtil.typeTextAndCloseKeyboard(onView(withId(R.id.old_passphrase)), oldPassphrase);
+ EspressoUtil.typeTextAndCloseKeyboard(onView(withId(R.id.new_passphrase)), newPassphrase);
+ EspressoUtil.typeTextAndCloseKeyboard(onView(withId(R.id.repeat_passphrase)), newPassphrase);
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityTest.java
new file mode 100644
index 0000000000..c49140c5a2
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/PassphraseChangeActivityTest.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragmentActions;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.not;
+
+@LargeTest
+public class PassphraseChangeActivityTest extends TextSecureEspressoTestCase {
+
+ private static final String ORIGINAL_PASSPHRASE = "badpass";
+ private static final String CHANGE_PASSPHRASE = "worse";
+
+ public PassphraseChangeActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkState() throws Exception {
+ EspressoUtil.actuallyCloseSoftKeyboard();
+
+ if (TextSecurePreferences.isPasswordDisabled(getContext())) {
+ onView(withId(R.id.old_passphrase)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.old_passphrase_label)).check(matches(not(isDisplayed())));
+ } else {
+ onView(withId(R.id.old_passphrase)).check(matches(isDisplayed()));
+ onView(withId(R.id.old_passphrase_label)).check(matches(isDisplayed()));
+ }
+
+ onView(withId(R.id.new_passphrase)).check(matches(isDisplayed()));
+ onView(withId(R.id.repeat_passphrase)).check(matches(isDisplayed()));
+ }
+
+ private void clickEnablePassphraseAndCheckState() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickSettings(getContext());
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ ApplicationPreferencesActivityActions.clickAppProtectionSetting();
+ AppProtectionPreferenceFragmentActions.clickEnablePassphrase();
+ EspressoUtil.waitOn(PassphraseChangeActivity.class);
+ checkState();
+ }
+
+ public void testEnablePassphrase() throws Exception {
+ clickEnablePassphraseAndCheckState();
+ PassphraseChangeActivityActions.typeNewPassphrase(ORIGINAL_PASSPHRASE);
+ PassphraseChangeActivityActions.clickOk();
+
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ assertTrue(!TextSecurePreferences.isPasswordDisabled(getContext()));
+ }
+
+ public void testEnablePassphraseCancel() throws Exception {
+ clickEnablePassphraseAndCheckState();
+ EspressoUtil.actuallyCloseSoftKeyboard();
+ PassphraseChangeActivityActions.clickCancel();
+
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ assertTrue(TextSecurePreferences.isPasswordDisabled(getContext()));
+ }
+
+ public void testEnablePassphraseDoesNotMatch() throws Exception {
+ clickEnablePassphraseAndCheckState();
+ PassphraseChangeActivityActions.typeNewPassphrase(ORIGINAL_PASSPHRASE, "nope");
+ PassphraseChangeActivityActions.clickOk();
+ PassphraseChangeActivityActions.clickCancel();
+
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ assertTrue(TextSecurePreferences.isPasswordDisabled(getContext()));
+ }
+
+ private void clickChangePassphraseAndCheckState() throws Exception {
+ testEnablePassphrase();
+ AppProtectionPreferenceFragmentActions.clickChangePassphrase();
+ EspressoUtil.waitOn(PassphraseChangeActivity.class);
+ checkState();
+ }
+
+ public void testChangePassphrase() throws Exception {
+ clickChangePassphraseAndCheckState();
+ PassphraseChangeActivityActions.typeChangePassphrase(ORIGINAL_PASSPHRASE, CHANGE_PASSPHRASE);
+ PassphraseChangeActivityActions.clickOk();
+
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ assertTrue(!TextSecurePreferences.isPasswordDisabled(getContext()));
+ }
+
+ public void testChangePassphraseCancel() throws Exception {
+ clickChangePassphraseAndCheckState();
+
+ EspressoUtil.actuallyCloseSoftKeyboard();
+ PassphraseChangeActivityActions.clickCancel();
+
+ EspressoUtil.waitOn(ApplicationPreferencesActivity.class);
+ assertTrue(!TextSecurePreferences.isPasswordDisabled(getContext()));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityActions.java
new file mode 100644
index 0000000000..99aa28feca
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityActions.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+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.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+public class RegistrationActivityActions {
+
+ public static void typeCountryCode(String countryCode) throws Exception {
+ EspressoUtil.replaceTextAndCloseKeyboard(onView(withId(R.id.country_code)), countryCode);
+ }
+
+ public static void typeLocalNumber(String localNumber) throws Exception {
+ EspressoUtil.replaceTextAndCloseKeyboard(onView(withId(R.id.number)), localNumber);
+ }
+
+ public static void clickRegister() throws Exception {
+ onView(withId(R.id.registerButton)).perform(click());
+ }
+
+ public static void clickCancel() throws Exception {
+ onView(withId(R.id.skipButton)).perform(click());
+ }
+
+ public static void clickContinue() throws Exception {
+ onView(withText(R.string.RegistrationActivity_continue)).perform(click());
+ }
+
+ public static void clickEdit() throws Exception {
+ onView(withText(R.string.RegistrationActivity_edit)).perform(click());
+ }
+
+ public static void enterPstnNumber(String pstnCountry, String pstnNumber) throws Exception {
+ typeCountryCode(pstnCountry);
+ typeLocalNumber(pstnNumber);
+ }
+
+ public static void sleepTillRegistrationConnected() {
+ long timeout = 0L;
+ while (timeout < 10000L) {
+ try {
+
+ Thread.sleep(1000);
+ timeout += 1000;
+ onView(withId(R.id.verification_progress)).check(matches(isDisplayed()));
+ return;
+
+ } catch (InterruptedException e) { }
+ catch (AssertionError e) { }
+ }
+ throw new AssertionError("failed to connect to registration servers within 10 seconds");
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityTest.java
new file mode 100644
index 0000000000..f8c112c5a0
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationActivityTest.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.not;
+
+@LargeTest
+public class RegistrationActivityTest extends TextSecureEspressoTestCase {
+
+ public RegistrationActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void loadStateAndCheck() throws Exception {
+ loadActivity(RegistrationActivity.class, STATE_BASE);
+ onView(ViewMatchers.withId(R.id.skipButton)).check(matches(not(isDisplayed())));
+ onView(withId(R.id.registerButton)).check(matches(isDisplayed()));
+ }
+
+ public void testRegister() throws Exception {
+ loadStateAndCheck();
+ RegistrationActivityActions.enterPstnNumber(pstnCountry, pstnNumber);
+ sleepThroughRegistrationLimit();
+ RegistrationActivityActions.clickRegister();
+ RegistrationActivityActions.clickContinue();
+ EspressoUtil.waitOn(RegistrationProgressActivity.class);
+ RegistrationActivityActions.sleepTillRegistrationConnected();
+ RegistrationBypassUtil.receiveVerificationSms(getContext(), pstnCountry, pstnNumber, verificationCode);
+ EspressoUtil.waitOn(ConversationListActivity.class);
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationBypassUtil.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationBypassUtil.java
new file mode 100644
index 0000000000..be220292d7
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/RegistrationBypassUtil.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.util.Log;
+
+import org.thoughtcrime.securesms.service.SmsListener;
+import org.thoughtcrime.securesms.util.SmsUtil;
+import org.thoughtcrime.securesms.test.R;
+
+public class RegistrationBypassUtil {
+
+ private static final String TAG = RegistrationBypassUtil.class.getSimpleName();
+
+ private static String getKeyForDevice() {
+ Log.d(TAG, "release: " + VERSION.RELEASE + ", model: " + Build.MODEL);
+ return VERSION.RELEASE + " - " + Build.MODEL;
+ }
+
+ public static String getPstnStringForDevice(Context context) {
+ final String DEVICE_KEY = getKeyForDevice();
+ final String[] TEST_DEVICES = context.getResources().getStringArray(R.array.test_devices);
+ final String[] PSTN_STRINGS = context.getResources().getStringArray(R.array.test_pstn_numbers);
+ int deviceIndex = -1;
+
+ if (TEST_DEVICES.length == 0 || PSTN_STRINGS.length != TEST_DEVICES.length) {
+ throw new AssertionError("one test device per pstn number required");
+ }
+
+ for (int i = 0; i < TEST_DEVICES.length; i++) {
+ if (TEST_DEVICES[i].equals(DEVICE_KEY)) {
+ deviceIndex = i;
+ break;
+ }
+ }
+
+ if (deviceIndex < 0) return PSTN_STRINGS[PSTN_STRINGS.length - 1];
+ else return PSTN_STRINGS[deviceIndex];
+ }
+
+ public static String getVerificationCodeForPstnString(Context context, String pstnString) {
+ final String[] PSTN_STRINGS = context.getResources().getStringArray(R.array.test_pstn_numbers);
+ final String[] VERIFY_CODES = context.getResources().getStringArray(R.array.test_verification_codes);
+ int pstnIndex = -1;
+
+ if (PSTN_STRINGS.length == 0 || PSTN_STRINGS.length != VERIFY_CODES.length) {
+ throw new AssertionError("one verification code per pstn number required");
+ }
+
+ for (int i = 0; i < PSTN_STRINGS.length; i++) {
+ if (PSTN_STRINGS[i].equals(pstnString)) {
+ pstnIndex = i;
+ break;
+ }
+ }
+
+ if (pstnIndex < 0) throw new AssertionError("no verification code for " + pstnString);
+ else return VERIFY_CODES[pstnIndex];
+ }
+
+ public static void receiveVerificationSms(Context context,
+ String pstnCountry,
+ String pstnNumber,
+ String verificationCode)
+ throws Exception
+ {
+ final String smsVerifyMessage = "Your TextSecure verification code: " + verificationCode;
+ final Intent smsVerifyIntent = SmsUtil.buildSmsReceivedIntent(pstnCountry + pstnNumber, smsVerifyMessage);
+
+ try {
+ new SmsListener().onReceive(context, smsVerifyIntent);
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "some api levels are picky with abortBroadcast()", e);
+ }
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureEspressoTestCase.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureEspressoTestCase.java
new file mode 100644
index 0000000000..05f603af6b
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureEspressoTestCase.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.app.Activity;
+import android.content.Context;
+import android.preference.PreferenceManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+public class TextSecureEspressoTestCase extends ActivityInstrumentationTestCase2 {
+
+ private static final String TAG = TextSecureEspressoTestCase.class.getSimpleName();
+
+ public static final int STATE_BASE = 0x00000000;
+ public static final int STATE_REGISTRATION_SKIPPED = 0x00000001;
+ public static final int STATE_REGISTERED = 0x00000002;
+
+ private static final long REGISTRATION_RATE_LIMIT_MS = 60000 + 10000;
+ private static long TIME_LAST_REGISTERED = 0L;
+
+ protected String pstnCountry;
+ protected String pstnNumber;
+ protected String verificationCode;
+
+ public TextSecureEspressoTestCase(Class clazz) {
+ super(clazz);
+ }
+
+ protected static void sleepThroughRegistrationLimit() {
+ long msPassedSinceReg = System.currentTimeMillis() - TIME_LAST_REGISTERED;
+ long msSleepRemaining = REGISTRATION_RATE_LIMIT_MS - msPassedSinceReg;
+
+ Log.d(TAG, "sleeping for " + msSleepRemaining + "ms to avoid registration rate limit");
+ while (msSleepRemaining > 0) {
+ try {
+
+ Thread.sleep(1000);
+ msSleepRemaining -= 1000;
+
+ } catch (InterruptedException e) { }
+ }
+ TIME_LAST_REGISTERED = System.currentTimeMillis();
+ }
+
+ protected Context getContext() {
+ return getInstrumentation().getTargetContext();
+ }
+
+ private void initBaseState() throws Exception {
+ EspressoUtil.removeAllContacts(getContext());
+ DatabaseFactory.getDraftDatabase(getContext()).clearAllDrafts();
+ DatabaseFactory.getThreadDatabase(getContext()).deleteAllConversations();
+ PreferenceManager.getDefaultSharedPreferences(getContext()).edit().clear().commit();
+ getContext().getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0).edit().clear().commit();
+ getContext().getSharedPreferences("SecureSMS", 0).edit().clear().commit();
+ }
+
+ protected void loadActivity(Class extends Activity> clazz, int state) throws Exception {
+ switch (state) {
+ case STATE_REGISTRATION_SKIPPED:
+ TextSecurePreferences.setPromptedPushRegistration(getContext(), true);
+ getActivity();
+ break;
+
+ case STATE_REGISTERED:
+ getActivity();
+ EspressoUtil.waitOn(RegistrationActivity.class);
+ RegistrationActivityActions.enterPstnNumber(pstnCountry, pstnNumber);
+ sleepThroughRegistrationLimit();
+ RegistrationActivityActions.clickRegister();
+ RegistrationActivityActions.clickContinue();
+ EspressoUtil.waitOn(RegistrationProgressActivity.class);
+ RegistrationActivityActions.sleepTillRegistrationConnected();
+ RegistrationBypassUtil.receiveVerificationSms(getContext(), pstnCountry, pstnNumber, verificationCode);
+ break;
+
+ default:
+ getActivity();
+ }
+
+ EspressoUtil.waitOn(clazz);
+ }
+
+ private void initMockPstnState() throws Exception {
+ final Context context = getInstrumentation().getContext();
+ final String pstnString = RegistrationBypassUtil.getPstnStringForDevice(context);
+
+ pstnCountry = pstnString.split(":")[0].replace("+", "");
+ pstnNumber = pstnString.split(":")[1];
+ verificationCode = RegistrationBypassUtil.getVerificationCodeForPstnString(context, pstnString);
+
+ Log.d(TAG, "using pstn id of " + pstnString + " with verification code " + verificationCode);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());
+ super.setUp();
+
+ if (pstnCountry == null || pstnNumber == null || verificationCode == null) {
+ initMockPstnState();
+ }
+ initBaseState();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ EspressoUtil.actuallyCloseSoftKeyboard();
+ EspressoUtil.closeAllActivities();
+ super.tearDown();
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureWakingTestRunner.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureWakingTestRunner.java
new file mode 100644
index 0000000000..0e439a21dc
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/TextSecureWakingTestRunner.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.app.Application;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.os.PowerManager;
+import android.support.test.runner.AndroidJUnitRunner;
+import android.util.Log;
+
+public class TextSecureWakingTestRunner extends AndroidJUnitRunner {
+
+ @Override public void onStart() {
+ runOnMainSync(new Runnable() {
+ @Override public void run() {
+ Application app = (Application) getTargetContext().getApplicationContext();
+ String simpleName = TextSecureWakingTestRunner.class.getSimpleName();
+
+ ((KeyguardManager) app.getSystemService(Context.KEYGUARD_SERVICE))
+ .newKeyguardLock(simpleName)
+ .disableKeyguard();
+
+ ((PowerManager) app.getSystemService(Context.POWER_SERVICE))
+ .newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, simpleName)
+ .acquire();
+ }
+ });
+ super.onStart();
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewLocalIdentityActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewLocalIdentityActivityTest.java
new file mode 100644
index 0000000000..0382aa2c73
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewLocalIdentityActivityTest.java
@@ -0,0 +1,48 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
+import org.whispersystems.libaxolotl.IdentityKey;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+@LargeTest
+public class ViewLocalIdentityActivityTest extends TextSecureEspressoTestCase {
+
+ public ViewLocalIdentityActivityTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void clickMyIdentity() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickMyIdentity(getContext());
+ EspressoUtil.waitOn(ViewLocalIdentityActivity.class);
+ }
+
+ public void testLayout() throws Exception {
+ clickMyIdentity();
+ final IdentityKey idKey = IdentityKeyUtil.getIdentityKey(getContext());
+ onView(ViewMatchers.withId(R.id.identity_fingerprint)).check(matches(withText(idKey.getFingerprint())));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewMatchers.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewMatchers.java
new file mode 100644
index 0000000000..bcabb0e407
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ViewMatchers.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+public class ViewMatchers {
+
+ public static Matcher withRecyclerItem(final Matcher itemMatcher) {
+ return new TypeSafeMatcher() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with recycler item: ");
+ itemMatcher.describeTo(description);
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ if (!(view instanceof RecyclerView)) {
+ return false;
+ }
+
+ RecyclerView recyclerView = ((RecyclerView) view);
+ for (int i = 0; i < recyclerView.getChildCount(); i++) {
+ if (itemMatcher.matches(recyclerView.getChildAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
+ public static Matcher withRecyclerItemCount(final long itemCount) {
+ return new TypeSafeMatcher() {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("with recycler item count: " + itemCount);
+ }
+
+ @Override
+ public boolean matchesSafely(View view) {
+ if (!(view instanceof RecyclerView)) {
+ return false;
+ }
+
+ RecyclerView recyclerView = ((RecyclerView) view);
+ return recyclerView.getChildCount() == itemCount;
+ }
+ };
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentActions.java
new file mode 100644
index 0000000000..2fad603548
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentActions.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class AdvancedPreferenceFragmentActions {
+
+ public static void clickTextSecureMessages() throws Exception {
+ onData(Matchers.allOf(withKey("pref_toggle_push_messaging"))).perform(click());
+ }
+
+ public static void clickEnterKeySends() throws Exception {
+ onData(Matchers.allOf(withKey("pref_enter_sends"))).perform(click());
+ }
+
+ public static void clickChooseIdentity() throws Exception {
+ onData(Matchers.allOf(withKey("pref_choose_identity"))).perform(click());
+ }
+
+ public static void clickSubmitDebugLog() throws Exception {
+ onData(Matchers.allOf(withKey("pref_submit_debug_logs"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentTest.java
new file mode 100644
index 0000000000..c2a4e78ebb
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AdvancedPreferenceFragmentTest.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.hamcrest.Matchers;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
+import org.thoughtcrime.securesms.ConversationListActivity;
+import org.thoughtcrime.securesms.LogSubmitActivity;
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.RegistrationActivity;
+import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivityActions;
+import org.thoughtcrime.securesms.ConversationListActivityActions;
+import org.thoughtcrime.securesms.TextSecureEspressoTestCase;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onData;
+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.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.thoughtcrime.securesms.EspressoUtil.waitOn;
+
+@LargeTest
+public class AdvancedPreferenceFragmentTest extends TextSecureEspressoTestCase {
+
+ public AdvancedPreferenceFragmentTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkAllPreferencesDisplayed() throws Exception {
+ onData(Matchers.allOf(withKey("pref_toggle_push_messaging")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_enter_sends")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_submit_debug_logs")))
+ .check(matches(isDisplayed()));
+
+ ContactIdentityManager identity = ContactIdentityManager.getInstance(getContext());
+ if (!identity.isSelfIdentityAutoDetected()) {
+ onData(Matchers.allOf(withKey("pref_choose_identity")))
+ .check(matches(isDisplayed()));
+ }
+ }
+
+ private void checkViewsMatchPreferences() throws Exception {
+ if (TextSecurePreferences.isPushRegistered(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_toggle_push_messaging"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_toggle_push_messaging"))));
+ }
+
+ if (TextSecurePreferences.isEnterSendsEnabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_enter_sends"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_enter_sends"))));
+ }
+ }
+
+ private void clickAdvancedSettingAndCheckState() throws Exception {
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ ApplicationPreferencesActivityActions.clickAdvancedSetting();
+
+ checkAllPreferencesDisplayed();
+ checkViewsMatchPreferences();
+ }
+
+ public void testEnableTextSecureMessages() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ clickAdvancedSettingAndCheckState();
+ AdvancedPreferenceFragmentActions.clickTextSecureMessages();
+ waitOn(RegistrationActivity.class);
+ }
+
+ public void testDisableTextSecureMessages() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTERED);
+ clickAdvancedSettingAndCheckState();
+ AdvancedPreferenceFragmentActions.clickTextSecureMessages();
+ onView(withText(R.string.ApplicationPreferencesActivity_disable_push_messages))
+ .check(matches(isDisplayed()));
+ onView(withText(android.R.string.cancel)).perform(click());
+ }
+
+ public void testSubmitDebugLog() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ clickAdvancedSettingAndCheckState();
+ AdvancedPreferenceFragmentActions.clickSubmitDebugLog();
+ waitOn(LogSubmitActivity.class);
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentActions.java
new file mode 100644
index 0000000000..323c7b124c
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentActions.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+import org.thoughtcrime.securesms.R;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+public class AppProtectionPreferenceFragmentActions {
+
+ public static void clickEnablePassphrase() throws Exception {
+ onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))).perform(click());
+ }
+
+ public static void disablePassphrase() throws Exception {
+ clickEnablePassphrase();
+ onView(withText(R.string.ApplicationPreferencesActivity_disable)).perform(click());
+ }
+
+ public static void clickChangePassphrase() throws Exception {
+ onData(Matchers.allOf(withKey("pref_change_passphrase"))).perform(click());
+ }
+
+ public static void clickTimeoutPassphrase() throws Exception {
+ onData(Matchers.allOf(withKey("pref_timeout_passphrase"))).perform(click());
+ }
+
+ public static void clickScreenSecurity() throws Exception {
+ onData(Matchers.allOf(withKey("pref_screen_security"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentTest.java
new file mode 100644
index 0000000000..3cc9167e19
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppProtectionPreferenceFragmentTest.java
@@ -0,0 +1,136 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.hamcrest.Matchers;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
+import org.thoughtcrime.securesms.ConversationListActivity;
+import org.thoughtcrime.securesms.PassphraseChangeActivity;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivityActions;
+import org.thoughtcrime.securesms.ConversationListActivityActions;
+import org.thoughtcrime.securesms.PassphraseChangeActivityActions;
+import org.thoughtcrime.securesms.TextSecureEspressoTestCase;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static org.thoughtcrime.securesms.EspressoUtil.waitOn;
+
+@LargeTest
+public class AppProtectionPreferenceFragmentTest extends TextSecureEspressoTestCase {
+
+ private static final String ORIGINAL_PASSPHRASE = "badpass";
+
+ public AppProtectionPreferenceFragmentTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkAllPreferencesDisplayed() throws Exception {
+ onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))).check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_change_passphrase"))).check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_timeout_passphrase"))).check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_timeout_interval"))).check(matches(isDisplayed()));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ onData(Matchers.allOf(withKey("pref_screen_security")))
+ .check(matches(isDisplayed()));
+ }
+ }
+
+ private void checkViewsMatchPreferences() throws Exception {
+ if (!TextSecurePreferences.isPasswordDisabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))));
+ }
+
+ if (TextSecurePreferences.isPassphraseTimeoutEnabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_timeout_passphrase"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_timeout_passphrase"))));
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ if (TextSecurePreferences.isScreenSecurityEnabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_screen_security"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_screen_security"))));
+ }
+ }
+ }
+
+ private void checkState() throws Exception {
+ checkAllPreferencesDisplayed();
+ checkViewsMatchPreferences();
+ }
+
+ private void clickAppProtectionSettingAndCheckState() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ ApplicationPreferencesActivityActions.clickAppProtectionSetting();
+ checkState();
+ }
+
+ public void testEnablePassphrase() throws Exception {
+ clickAppProtectionSettingAndCheckState();
+
+ AppProtectionPreferenceFragmentActions.clickEnablePassphrase();
+ waitOn(PassphraseChangeActivity.class);
+ PassphraseChangeActivityActions.typeNewPassphrase(ORIGINAL_PASSPHRASE);
+ PassphraseChangeActivityActions.clickOk();
+ waitOn(ApplicationPreferencesActivity.class);
+
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))));
+ checkState();
+ }
+
+ public void testDisablePassphrase() throws Exception {
+ testEnablePassphrase();
+ AppProtectionPreferenceFragmentActions.disablePassphrase();
+
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_enable_passphrase_temporary"))));
+ checkState();
+ }
+
+ public void testEnablePassphraseTimeout() throws Exception {
+ testEnablePassphrase();
+ AppProtectionPreferenceFragmentActions.clickTimeoutPassphrase();
+
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_timeout_passphrase"))));
+ checkState();
+ }
+
+ public void testDisableScreenSecurity() throws Exception {
+ clickAppProtectionSettingAndCheckState();
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return;
+ }
+
+ AppProtectionPreferenceFragmentActions.clickScreenSecurity();
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_screen_security"))));
+ checkState();
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentActions.java
new file mode 100644
index 0000000000..fd2bebed1b
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentActions.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class AppearancePreferenceFragmentActions {
+
+ public static void clickTheme() throws Exception {
+ onData(Matchers.allOf(withKey("pref_theme"))).perform(click());
+ }
+
+ public static void clickLanguage() throws Exception {
+ onData(Matchers.allOf(withKey("pref_language"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentTest.java
new file mode 100644
index 0000000000..679d36d653
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/AppearancePreferenceFragmentTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.hamcrest.Matchers;
+import org.thoughtcrime.securesms.ConversationListActivity;
+import org.thoughtcrime.securesms.TextSecureEspressoTestCase;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withSummaryText;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+@LargeTest
+public class AppearancePreferenceFragmentTest extends TextSecureEspressoTestCase {
+
+ public AppearancePreferenceFragmentTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkAllPreferencesDisplayed() throws Exception {
+ onData(Matchers.allOf(withKey("pref_theme")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_language")))
+ .check(matches(isDisplayed()));
+ }
+
+ private void checkViewsMatchPreferences() throws Exception {
+ // todo :|
+ final String theme = TextSecurePreferences.getTheme(getContext());
+ onData(Matchers.allOf(withKey("pref_theme"), withSummaryText(theme)))
+ .check(matches(isDisplayed()));
+
+ final String language = TextSecurePreferences.getTheme(getContext());
+ onData(Matchers.allOf(withKey("pref_language"), withSummaryText(language)))
+ .check(matches(isDisplayed()));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentActions.java
new file mode 100644
index 0000000000..a19e6ff83b
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentActions.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class NotificationsPreferenceFragmentActions {
+
+ public static void clickEnableNotifications() throws Exception {
+ onData(Matchers.allOf(withKey("pref_key_enable_notifications"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentTest.java
new file mode 100644
index 0000000000..d9371cf8d4
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/NotificationsPreferenceFragmentTest.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.hamcrest.Matchers;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
+import org.thoughtcrime.securesms.ConversationListActivity;
+import org.thoughtcrime.securesms.ApplicationPreferencesActivityActions;
+import org.thoughtcrime.securesms.ConversationListActivityActions;
+import org.thoughtcrime.securesms.TextSecureEspressoTestCase;
+import org.thoughtcrime.securesms.util.TextSecurePreferences;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+import static android.support.test.espresso.matcher.ViewMatchers.isChecked;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static org.thoughtcrime.securesms.EspressoUtil.waitOn;
+
+@LargeTest
+public class NotificationsPreferenceFragmentTest extends TextSecureEspressoTestCase {
+
+ public NotificationsPreferenceFragmentTest() {
+ super(ConversationListActivity.class);
+ }
+
+ private void checkAllPreferencesDisplayed() throws Exception {
+ onData(Matchers.allOf(withKey("pref_key_enable_notifications")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_key_ringtone")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_key_vibrate")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_led_color")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_led_blink")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_key_inthread_notifications")))
+ .check(matches(isDisplayed()));
+ onData(Matchers.allOf(withKey("pref_repeat_alerts")))
+ .check(matches(isDisplayed()));
+ }
+
+ private void checkViewsMatchPreferences() throws Exception {
+ if (TextSecurePreferences.isNotificationsEnabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_key_enable_notifications"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_key_enable_notifications"))));
+ }
+
+ if (TextSecurePreferences.isNotificationVibrateEnabled(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_key_vibrate"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_key_vibrate"))));
+ }
+
+ if (TextSecurePreferences.isInThreadNotifications(getContext())) {
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_key_inthread_notifications"))));
+ } else {
+ isNotChecked().matches(onData(Matchers.allOf(withKey("pref_key_inthread_notifications"))));
+ }
+ }
+
+ private void clickNotificationsSettingAndCheckState() throws Exception {
+ loadActivity(ConversationListActivity.class, STATE_REGISTRATION_SKIPPED);
+ ConversationListActivityActions.clickSettings(getContext());
+ waitOn(ApplicationPreferencesActivity.class);
+ ApplicationPreferencesActivityActions.clickNotificationsSetting();
+
+ checkAllPreferencesDisplayed();
+ checkViewsMatchPreferences();
+ }
+
+ public void testEnableNotifications() throws Exception {
+ clickNotificationsSettingAndCheckState();
+ NotificationsPreferenceFragmentActions.clickEnableNotifications();
+ isChecked().matches(onData(Matchers.allOf(withKey("pref_key_enable_notifications"))));
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragmentActions.java
new file mode 100644
index 0000000000..c9f6b75f8e
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/SmsMmsPreferenceFragmentActions.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class SmsMmsPreferenceFragmentActions {
+
+ public static void clickReceiveAllSms() throws Exception {
+ onData(Matchers.allOf(withKey("pref_all_sms"))).perform(click());
+ }
+
+ public static void clickReceiveAllMms() throws Exception {
+ onData(Matchers.allOf(withKey("pref_all_mms"))).perform(click());
+ }
+
+ public static void clickSmsDeliveryReports() throws Exception {
+ onData(Matchers.allOf(withKey("pref_delivery_report_sms"))).perform(click());
+ }
+
+ public static void clickWifiCallingCompat() throws Exception {
+ onData(Matchers.allOf(withKey("pref_wifi_sms"))).perform(click());
+ }
+
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/StoragePreferenceFragmentActions.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/StoragePreferenceFragmentActions.java
new file mode 100644
index 0000000000..b1f3ad6759
--- /dev/null
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/preferences/StoragePreferenceFragmentActions.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2015 Open Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.thoughtcrime.securesms.preferences;
+
+import org.hamcrest.Matchers;
+
+import static android.support.test.espresso.Espresso.onData;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.PreferenceMatchers.withKey;
+
+public class StoragePreferenceFragmentActions {
+
+ public static void clickDeleteOldMessages() throws Exception {
+ onData(Matchers.allOf(withKey("pref_trim_threads"))).perform(click());
+ }
+
+ public static void clickConversationLengthLimit() throws Exception {
+ onData(Matchers.allOf(withKey("pref_trim_length"))).perform(click());
+ }
+
+ public static void clickTrimAllNow() throws Exception {
+ onData(Matchers.allOf(withKey("pref_trim_now"))).perform(click());
+ }
+
+}
diff --git a/test/espresso/AndroidManifest.xml b/test/espresso/AndroidManifest.xml
new file mode 100644
index 0000000000..65b7a6350b
--- /dev/null
+++ b/test/espresso/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+