From b01075cef6c544311834f629b2656ce4e2aa4866 Mon Sep 17 00:00:00 2001 From: Harris Date: Wed, 9 Feb 2022 14:18:22 +1100 Subject: [PATCH] fix: add UI test for URL modal dialog and fix mention infinite layout inflation bugs (#841) --- .../loki/messenger/HomeActivityTests.kt | 81 ++++++++++++++++++- .../conversation/v2/ConversationActivityV2.kt | 10 +-- .../v2/messages/VisibleMessageContentView.kt | 1 + .../res/layout/activity_conversation_v2.xml | 7 +- .../res/layout/view_mention_candidate_v2.xml | 4 +- 5 files changed, 86 insertions(+), 17 deletions(-) diff --git a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt index e59b49669..db977544c 100644 --- a/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt +++ b/app/src/androidTest/java/network/loki/messenger/HomeActivityTests.kt @@ -1,26 +1,44 @@ package network.loki.messenger +import android.app.Instrumentation import android.content.ClipboardManager import android.content.Context +import android.view.View import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.pressBack +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction 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.espresso.matcher.ViewMatchers.isCompletelyDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withSubstring +import androidx.test.espresso.matcher.ViewMatchers.withText 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.Matcher import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.not +import org.junit.After +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.utilities.TextSecurePreferences +import org.session.libsignal.utilities.guava.Optional +import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 +import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar import org.thoughtcrime.securesms.home.HomeActivity +import org.thoughtcrime.securesms.mms.GlideApp + @RunWith(AndroidJUnit4::class) @LargeTest @@ -29,10 +47,29 @@ class HomeActivityTests { @get:Rule var activityRule = ActivityScenarioRule(HomeActivity::class.java) - private fun sendMessage(messageToSend: String) { + private val activityMonitor = Instrumentation.ActivityMonitor(ConversationActivityV2::class.java.name, null, false) + + @Before + fun setUp() { + InstrumentationRegistry.getInstrumentation().addMonitor(activityMonitor) + } + + @After + fun tearDown() { + InstrumentationRegistry.getInstrumentation().removeMonitor(activityMonitor) + } + + private fun sendMessage(messageToSend: String, linkPreview: LinkPreview? = null) { // assume in chat activity onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend)) + if (linkPreview != null) { + val activity = activityMonitor.waitForActivity() as ConversationActivityV2 + val glide = GlideApp.with(activity) + activity.findViewById(R.id.inputBar).updateLinkPreviewDraft(glide, linkPreview) + } onView(allOf(isDescendantOfA(withId(R.id.inputBar)),inputButtonWithDrawable(R.drawable.ic_arrow_up))).perform(ViewActions.click()) + // TODO: text can flaky on cursor reload, figure out a better way to wait for the UI to settle with new data + onView(isRoot()).perform(waitFor(500)) } private fun setupLoggedInState(hasViewedSeed: Boolean = false) { @@ -99,7 +136,43 @@ class HomeActivityTests { // 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 + } + + @Test + fun testChat_displaysCorrectUrl() { + setupLoggedInState() + goToMyChat() + TextSecurePreferences.setLinkPreviewsEnabled(InstrumentationRegistry.getInstrumentation().targetContext, true) + // given the link url text + val url = "https://www.ámazon.com" + sendMessage(url, LinkPreview(url, "amazon", Optional.absent())) + + // when the URL span is clicked + onView(withSubstring(url)).perform(ViewActions.click()) + + // then the URL dialog should be displayed with a known punycode url + val amazonPuny = "https://www.xn--mazon-wqa.com/" + + val dialogPromptText = InstrumentationRegistry.getInstrumentation().targetContext.getString(R.string.dialog_open_url_explanation, amazonPuny) + + onView(withText(dialogPromptText)).check(matches(isDisplayed())) + } + + /** + * Perform action of waiting for a specific time. + */ + fun waitFor(millis: Long): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher? { + return isRoot() + } + + override fun getDescription(): String = "Wait for $millis milliseconds." + + override fun perform(uiController: UiController, view: View?) { + uiController.loopMainThreadForAtLeast(millis) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index fc181d79c..9b857b7f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -622,6 +622,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val query = text.substring(currentMentionStartIndex + 1) // + 1 to get rid of the "@" showOrUpdateMentionCandidatesIfNeeded(query) } + } else { + currentMentionStartIndex = -1 + hideMentionCandidates() } previousText = text } @@ -636,13 +639,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val candidates = MentionsManager.getMentionCandidates(query, viewModel.threadId, viewModel.recipient.isOpenGroupRecipient) this.mentionCandidatesView = view view.show(candidates, viewModel.threadId) - view.alpha = 0.0f - val animation = ValueAnimator.ofObject(FloatEvaluator(), view.alpha, 1.0f) - animation.duration = 250L - animation.addUpdateListener { animator -> - view.alpha = animator.animatedValue as Float - } - animation.start() } else { val candidates = MentionsManager.getMentionCandidates(query, viewModel.threadId, viewModel.recipient.isOpenGroupRecipient) this.mentionCandidatesView!!.setMentionCandidates(candidates) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt index 63cfbbda2..0c4c3d81a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageContentView.kt @@ -93,6 +93,7 @@ class VisibleMessageContentView : LinearLayout { binding.quoteView.isVisible = message is MmsMessageRecord && message.quote != null binding.linkPreviewView.isVisible = message is MmsMessageRecord && message.linkPreviews.isNotEmpty() + binding.linkPreviewView.bodyTextView = binding.bodyTextView val linkPreviewLayout = binding.linkPreviewView.layoutParams linkPreviewLayout.width = if (mediaThumbnailMessage) 0 else ViewGroup.LayoutParams.WRAP_CONTENT diff --git a/app/src/main/res/layout/activity_conversation_v2.xml b/app/src/main/res/layout/activity_conversation_v2.xml index 65652359c..d14e250ee 100644 --- a/app/src/main/res/layout/activity_conversation_v2.xml +++ b/app/src/main/res/layout/activity_conversation_v2.xml @@ -16,9 +16,9 @@ + android:layout_height="match_parent" + android:layout_above="@+id/typingIndicatorViewContainer" /> + android:layout_alignBottom="@+id/conversationRecyclerView"/> - - \ No newline at end of file + \ No newline at end of file