diff --git a/app/build.gradle b/app/build.gradle
index d883f4ab68..0fbafe0fc6 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -127,19 +127,30 @@ dependencies {
testImplementation 'org.powermock:powermock-module-junit4-rule:1.6.1'
testImplementation 'org.powermock:powermock-classloading-xstream:1.6.1'
testImplementation 'androidx.test:core:1.3.0'
- androidTestImplementation 'androidx.multidex:multidex:2.0.1'
- androidTestImplementation 'androidx.multidex:multidex-instrumentation:2.0.0'
- androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
- androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
- androidTestImplementation ('org.assertj:assertj-core:1.7.1') {
- exclude group: 'org.hamcrest', module: 'hamcrest-core'
- }
- androidTestImplementation ('com.squareup.assertj:assertj-android:1.1.1') {
- exclude group: 'org.hamcrest', module: 'hamcrest-core'
- exclude group: 'com.android.support', module: 'support-annotations'
- }
- testImplementation 'org.robolectric:robolectric:4.2.1'
- testImplementation 'org.robolectric:shadows-multidex:4.2'
+ // Core library
+ androidTestImplementation 'androidx.test:core:1.4.0'
+
+ // AndroidJUnitRunner and JUnit Rules
+ androidTestImplementation 'androidx.test:runner:1.4.0'
+ androidTestImplementation 'androidx.test:rules:1.4.0'
+
+ // Assertions
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.ext:truth:1.4.0'
+ androidTestImplementation 'com.google.truth:truth:1.0'
+
+ // Espresso dependencies
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-accessibility:3.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-web:3.4.0'
+ androidTestImplementation 'androidx.test.espresso.idling:idling-concurrent:3.4.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.4.0'
+ androidTestUtil 'androidx.test:orchestrator:1.4.0'
+
+ testImplementation 'org.robolectric:robolectric:4.4'
+ testImplementation 'org.robolectric:shadows-multidex:4.4'
}
def canonicalVersionCode = 217
@@ -209,6 +220,14 @@ android {
buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
resConfigs autoResConfig()
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ // The following argument makes the Android Test Orchestrator run its
+ // "pm clear" command after each test invocation. This command ensures
+ // that the app's state is completely cleared between tests.
+ testInstrumentationRunnerArguments clearPackageData: 'true'
+ testOptions {
+ execution 'ANDROIDX_TEST_ORCHESTRATOR'
+ }
}
buildTypes {
diff --git a/app/src/androidTest/AndroidManifest.xml b/app/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000000..68f81f6f87
--- /dev/null
+++ b/app/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt
new file mode 100644
index 0000000000..43652b09e9
--- /dev/null
+++ b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt
@@ -0,0 +1,100 @@
+package network.loki.messenger
+
+import android.content.ClipboardManager
+import android.content.Context
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.Espresso.pressBack
+import androidx.test.espresso.action.ViewActions
+import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.*
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.test.platform.app.InstrumentationRegistry
+import network.loki.messenger.util.InputBarButtonDrawableMatcher.Companion.inputButtonWithDrawable
+import network.loki.messenger.util.NewConversationButtonDrawableMatcher.Companion.newConversationButtonWithDrawable
+import org.hamcrest.Matchers.allOf
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.session.libsession.utilities.TextSecurePreferences
+import org.thoughtcrime.securesms.home.HomeActivity
+
+@RunWith(AndroidJUnit4::class)
+@LargeTest
+class HomeActivityTests {
+
+ @get:Rule
+ var activityRule = ActivityScenarioRule(HomeActivity::class.java)
+
+ private fun sendMessage(messageToSend: String) {
+ // assume in chat activity
+ onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend))
+ onView(allOf(isDescendantOfA(withId(R.id.inputBar)),inputButtonWithDrawable(R.drawable.ic_arrow_up))).perform(ViewActions.click())
+ }
+
+ private fun setupLoggedInState(hasViewedSeed: Boolean = false) {
+ // landing activity
+ onView(withId(R.id.registerButton)).perform(ViewActions.click())
+ // session ID - register activity
+ onView(withId(R.id.registerButton)).perform(ViewActions.click())
+ // display name selection
+ onView(withId(R.id.displayNameEditText)).perform(ViewActions.typeText("test-user123"))
+ onView(withId(R.id.registerButton)).perform(ViewActions.click())
+ // PN select
+ if (hasViewedSeed) {
+ // has viewed seed is set to false after register activity
+ TextSecurePreferences.setHasViewedSeed(InstrumentationRegistry.getInstrumentation().targetContext, true)
+ }
+ onView(withId(R.id.backgroundPollingOptionView)).perform(ViewActions.click())
+ onView(withId(R.id.registerButton)).perform(ViewActions.click())
+ }
+
+ private fun goToMyChat() {
+ onView(newConversationButtonWithDrawable(R.drawable.ic_plus)).perform(ViewActions.click())
+ onView(newConversationButtonWithDrawable(R.drawable.ic_message)).perform(ViewActions.click())
+ // new chat
+ onView(withId(R.id.copyButton)).perform(ViewActions.click())
+ val context = InstrumentationRegistry.getInstrumentation().targetContext
+ val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ val copied = clipboardManager.primaryClip!!.getItemAt(0).text
+ onView(withId(R.id.publicKeyEditText)).perform(ViewActions.typeText(copied.toString()))
+ onView(withId(R.id.createPrivateChatButton)).perform(ViewActions.click())
+ }
+
+ @Test
+ fun testLaunches_dismiss_seedView() {
+ setupLoggedInState()
+ onView(allOf(withId(R.id.button), isDescendantOfA(withId(R.id.seedReminderView)))).perform(ViewActions.click())
+ onView(withId(R.id.copyButton)).perform(ViewActions.click())
+ pressBack()
+ onView(withId(R.id.seedReminderView)).check(matches(withEffectiveVisibility(Visibility.GONE)))
+ }
+
+ @Test
+ fun testIsVisible_seedView() {
+ setupLoggedInState()
+ onView(withId(R.id.seedReminderView)).check(matches(isCompletelyDisplayed()))
+ }
+
+ @Test
+ fun testIsVisible_alreadyDismissed_seedView() {
+ setupLoggedInState(hasViewedSeed = true)
+ onView(withId(R.id.seedReminderView)).check(doesNotExist())
+ }
+
+ @Test
+ fun testChat_withSelf() {
+ setupLoggedInState()
+ goToMyChat()
+ TextSecurePreferences.setLinkPreviewsEnabled(InstrumentationRegistry.getInstrumentation().targetContext, true)
+ sendMessage("howdy")
+ sendMessage("test")
+ // tests url rewriter doesn't crash
+ sendMessage("https://www.getsession.org?random_query_parameter=testtesttesttesttesttesttesttest&other_query_parameter=testtesttesttesttesttesttesttest")
+ sendMessage("https://www.ámazon.com")
+ // TODO: check data / tap URL and check it's displayed properly here
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/network/loki/messenger/util/Matchers.kt b/app/src/androidTest/java/network/loki/messenger/util/Matchers.kt
new file mode 100644
index 0000000000..1c93745467
--- /dev/null
+++ b/app/src/androidTest/java/network/loki/messenger/util/Matchers.kt
@@ -0,0 +1,42 @@
+package network.loki.messenger.util
+
+import android.view.View
+import androidx.annotation.DrawableRes
+import org.hamcrest.Description
+import org.hamcrest.TypeSafeMatcher
+import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
+import org.thoughtcrime.securesms.home.NewConversationButtonSetView
+
+class NewConversationButtonDrawableMatcher(@DrawableRes private val expectedId: Int): TypeSafeMatcher() {
+
+ companion object {
+ @JvmStatic fun newConversationButtonWithDrawable(@DrawableRes expectedId: Int) = NewConversationButtonDrawableMatcher(expectedId)
+ }
+
+ override fun describeTo(description: Description?) {
+ description?.appendText("with drawable on button with resource id: $expectedId")
+ }
+
+ override fun matchesSafely(item: View): Boolean {
+ if (item !is NewConversationButtonSetView.Button) return false
+
+ return item.getIconID() == expectedId
+ }
+}
+
+class InputBarButtonDrawableMatcher(@DrawableRes private val expectedId: Int): TypeSafeMatcher() {
+
+ companion object {
+ @JvmStatic fun inputButtonWithDrawable(@DrawableRes expectedId: Int) = InputBarButtonDrawableMatcher(expectedId)
+ }
+
+ override fun describeTo(description: Description?) {
+ description?.appendText("with drawable on button with resource id: $expectedId")
+ }
+
+ override fun matchesSafely(item: View): Boolean {
+ if (item !is InputBarButton) return false
+
+ return item.getIconID() == expectedId
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt
index 25e209424e..abf6720aa0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/input_bar/InputBarButton.kt
@@ -9,7 +9,6 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
-import android.util.Log
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
@@ -18,10 +17,7 @@ import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import network.loki.messenger.R
import org.thoughtcrime.securesms.util.*
-import org.thoughtcrime.securesms.util.GlowViewUtilities
-import org.thoughtcrime.securesms.util.InputBarButtonImageViewContainer
import java.util.*
-import kotlin.math.abs
class InputBarButton : RelativeLayout {
private val gestureHandler = Handler(Looper.getMainLooper())
@@ -105,6 +101,8 @@ class InputBarButton : RelativeLayout {
isHapticFeedbackEnabled = true
}
+ fun getIconID() = iconID
+
fun expand() {
GlowViewUtilities.animateColorChange(context, imageViewContainer, colorID, R.color.accent)
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_collapsed_size, R.dimen.input_bar_button_expanded_size, animationDuration)
diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt
index d8bc59d7da..d9886d2c32 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt
@@ -20,6 +20,7 @@ import androidx.loader.content.Loader
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_home.*
+import kotlinx.android.synthetic.main.seed_reminder_stub.*
import kotlinx.android.synthetic.main.seed_reminder_stub.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
@@ -168,7 +169,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
profileButton.update()
val hasViewedSeed = TextSecurePreferences.getHasViewedSeed(this)
if (hasViewedSeed) {
- seedReminderStub.visibility = View.GONE
+ seedReminderView?.isVisible = false
}
if (TextSecurePreferences.getConfigurationMessageSynced(this)) {
lifecycleScope.launch(Dispatchers.IO) {
diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt b/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt
index 1bd4df55d9..f414cba54a 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/home/NewConversationButtonSetView.kt
@@ -17,8 +17,6 @@ import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import network.loki.messenger.R
import org.thoughtcrime.securesms.util.*
-import org.thoughtcrime.securesms.util.GlowViewUtilities
-import org.thoughtcrime.securesms.util.NewConversationButtonImageView
class NewConversationButtonSetView : RelativeLayout {
private var expandedButton: Button? = null
@@ -52,6 +50,8 @@ class NewConversationButtonSetView : RelativeLayout {
@DrawableRes private var iconID = 0
private var isMain = false
+ fun getIconID() = iconID
+
companion object {
val animationDuration = 250.toLong()
}