Skip to content

Commit

Permalink
[CI] Add MessageList E2E tests
Browse files Browse the repository at this point in the history
  • Loading branch information
testableapple committed Dec 18, 2024
1 parent edc8543 commit 15fa235
Show file tree
Hide file tree
Showing 15 changed files with 1,193 additions and 50 deletions.
13 changes: 9 additions & 4 deletions fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ before_all do |lane|
end
end

lane :start_mock_server do
stop_mock_server if is_localhost
lane :start_mock_server do |options|
mock_server_repo = 'stream-chat-test-mock-server'
sh("rm -rf #{mock_server_repo}") if File.directory?(mock_server_repo)
sh("git clone [email protected]:#{github_repo.split('/').first}/#{mock_server_repo}.git")
stop_mock_server if is_localhost

if options[:local_server]
mock_server_repo = options[:local_server]
else
sh("rm -rf #{mock_server_repo}") if File.directory?(mock_server_repo)
sh("git clone [email protected]:#{github_repo.split('/').first}/#{mock_server_repo}.git")
end

Dir.chdir(mock_server_repo) do
FileUtils.mkdir_p('logs')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ open class MessageListPage {
class MessageList {

companion object {
val messageList = By.res("Stream_MessageList")
val messages = By.res("Stream_MessageCell")
val dateSeparator = By.res("Stream_MessageDateSeparator")
val unreadMessagesBadge = By.res("Stream_UnreadMessagesBadge")
Expand All @@ -90,6 +91,7 @@ open class MessageListPage {
val readStatusIsRead = By.res("Stream_MessageReadStatus_isRead")
val readStatusIsPending = By.res("Stream_MessageReadStatus_isPending")
val readStatusIsSent = By.res("Stream_MessageReadStatus_isSent")
val failedIcon = By.res("Stream_MessageFailedIcon")
val readCount = By.res("Stream_MessageReadCount")
val timestamp = By.res("Stream_Timestamp")
val reactions = By.res("Stream_MessageReaction")
Expand Down Expand Up @@ -145,6 +147,7 @@ open class MessageListPage {

companion object {
val reply = By.res("Stream_ContextMenu_Reply")
val resend = By.res("Stream_ContextMenu_Resend")
val threadReply = By.res("Stream_ContextMenu_Thread reply")
val markAsUnread = By.res("Stream_ContextMenu_Mark as Unread")
val copy = By.res("Stream_ContextMenu_Copy Message")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ import io.getstream.chat.android.compose.pages.MessageListPage
import io.getstream.chat.android.compose.pages.MessageListPage.Composer
import io.getstream.chat.android.compose.pages.MessageListPage.MessageList
import io.getstream.chat.android.compose.pages.MessageListPage.MessageList.Message
import io.getstream.chat.android.compose.pages.MessageListPage.MessageList.Message.ContextMenu
import io.getstream.chat.android.compose.pages.ThreadPage
import io.getstream.chat.android.compose.uiautomator.defaultTimeout
import io.getstream.chat.android.compose.uiautomator.device
import io.getstream.chat.android.compose.uiautomator.findObject
import io.getstream.chat.android.compose.uiautomator.findObjects
import io.getstream.chat.android.compose.uiautomator.isDisplayed
import io.getstream.chat.android.compose.uiautomator.longPress
import io.getstream.chat.android.compose.uiautomator.swipeDown
import io.getstream.chat.android.compose.uiautomator.swipeUp
import io.getstream.chat.android.compose.uiautomator.tapOnScreenCenter
import io.getstream.chat.android.compose.uiautomator.typeText
import io.getstream.chat.android.compose.uiautomator.wait
import io.getstream.chat.android.compose.uiautomator.waitToAppear
Expand Down Expand Up @@ -87,17 +90,23 @@ class UserRobot {

fun deleteMessage(messageCellIndex: Int = 0, hard: Boolean = false): UserRobot {
openContextMenu(messageCellIndex)
Message.ContextMenu.delete.waitToAppear().click()
ContextMenu.delete.waitToAppear().click()
return this
}

fun editMessage(newText: String, messageCellIndex: Int = 0): UserRobot {
openContextMenu(messageCellIndex)
Message.ContextMenu.edit.waitToAppear().click()
ContextMenu.edit.waitToAppear().click()
sendMessage(newText)
return this
}

fun resendMessage(messageCellIndex: Int = 0): UserRobot {
openContextMenu(messageCellIndex)
ContextMenu.resend.waitToAppear().click()
return this
}

fun clearComposer(): UserRobot {
Composer.inputField.waitToAppear().clear()
return this
Expand All @@ -121,15 +130,15 @@ class UserRobot {

fun quoteMessage(text: String, messageCellIndex: Int = 0): UserRobot {
openContextMenu(messageCellIndex)
Message.ContextMenu.reply.waitToAppear().click()
ContextMenu.reply.waitToAppear().click()
sendMessage(text)
return this
}

fun openThread(messageCellIndex: Int = 0, usingContextMenu: Boolean = true): UserRobot {
if (usingContextMenu) {
openContextMenu(messageCellIndex)
Message.ContextMenu.threadReply.waitToAppear().click()
ContextMenu.threadReply.waitToAppear().click()
} else {
Message.threadRepliesLabel.waitToAppear().click()
}
Expand Down Expand Up @@ -176,22 +185,22 @@ class UserRobot {
return this
}

fun scrollChannelListDown(times: Int = 1): UserRobot {
fun scrollChannelListDown(times: Int = 3): UserRobot {
device.swipeUp(times)
return this
}

fun scrollChannelListUp(times: Int = 1): UserRobot {
fun scrollChannelListUp(times: Int = 3): UserRobot {
device.swipeDown(times)
return this
}

fun scrollMessageListDown(times: Int = 1): UserRobot {
fun scrollMessageListDown(times: Int = 3): UserRobot {
scrollChannelListDown(times) // Reusing the channel list scroll
return this
}

fun scrollMessageListUp(times: Int = 1): UserRobot {
fun scrollMessageListUp(times: Int = 3): UserRobot {
scrollChannelListUp(times) // Reusing the channel list scroll
return this
}
Expand All @@ -206,6 +215,11 @@ class UserRobot {
return this
}

fun openAttachmentsMenu(): UserRobot {
Composer.attachmentsButton.waitToAppear().click()
return this
}

fun uploadGiphy(useComposerCommand: Boolean = false, send: Boolean = true): UserRobot {
val giphyMessageText = "G" // any message text will result in sending a giphy
if (useComposerCommand) {
Expand Down Expand Up @@ -273,4 +287,12 @@ class UserRobot {
}
return this
}

fun tapOnMessageList() {
if (MessageList.messageList.isDisplayed()) {
MessageList.messageList.waitToAppear().click()
} else {
device.tapOnScreenCenter()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,32 @@
package io.getstream.chat.android.compose.robots

import io.getstream.chat.android.compose.pages.ChannelListPage.ChannelList.Channel
import io.getstream.chat.android.compose.uiautomator.exists
import io.getstream.chat.android.compose.uiautomator.isDisplayed
import io.getstream.chat.android.compose.uiautomator.wait
import io.getstream.chat.android.compose.uiautomator.waitToAppear
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue

fun UserRobot.assertChannelAvatar(): UserRobot {
assertTrue(Channel.avatar.exists())
assertTrue(Channel.avatar.isDisplayed())
return this
}

fun UserRobot.assertMessageInChannelPreview(text: String, fromCurrentUser: Boolean): UserRobot {
val expectedPreview = if (fromCurrentUser) "You: $text" else text
assertEquals(expectedPreview, Channel.messagePreview.waitToAppear().text.trimEnd())
assertTrue(Channel.timestamp.exists())
assertTrue(Channel.timestamp.isDisplayed())
return this
}

fun UserRobot.assertMessageDeliveryStatus(shouldBeVisible: Boolean, shouldBeRead: Boolean = false): UserRobot {
if (shouldBeVisible) {
val readStatus = if (shouldBeRead) Channel.readStatusIsRead else Channel.readStatusIsSent
assertTrue(readStatus.wait().exists())
assertTrue(readStatus.wait().isDisplayed())
} else {
assertFalse(Channel.readStatusIsRead.exists())
assertFalse(Channel.readStatusIsSent.exists())
assertFalse(Channel.readStatusIsRead.isDisplayed())
assertFalse(Channel.readStatusIsSent.isDisplayed())
}
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,159 @@

package io.getstream.chat.android.compose.robots

import io.getstream.chat.android.compose.R
import io.getstream.chat.android.compose.pages.MessageListPage
import io.getstream.chat.android.compose.pages.MessageListPage.Composer
import io.getstream.chat.android.compose.pages.MessageListPage.MessageList.Message
import io.getstream.chat.android.compose.uiautomator.exists
import io.getstream.chat.android.compose.uiautomator.appContext
import io.getstream.chat.android.compose.uiautomator.findObject
import io.getstream.chat.android.compose.uiautomator.findObjects
import io.getstream.chat.android.compose.uiautomator.height
import io.getstream.chat.android.compose.uiautomator.isDisplayed
import io.getstream.chat.android.compose.uiautomator.wait
import io.getstream.chat.android.compose.uiautomator.waitForText
import io.getstream.chat.android.compose.uiautomator.waitToAppear
import io.getstream.chat.android.compose.uiautomator.waitToDisappear
import io.getstream.chat.android.e2e.test.mockserver.MessageReadStatus
import io.getstream.chat.android.e2e.test.robots.ParticipantRobot
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue

fun UserRobot.assertMessage(text: String): UserRobot {
assertEquals(text, Message.text.waitToAppear().text)
assertTrue(Message.timestamp.exists())
fun UserRobot.assertMessage(text: String, isDisplayed: Boolean = true): UserRobot {
if (isDisplayed) {
assertEquals(text, Message.text.waitToAppear().waitForText(text).text)
assertTrue(Message.text.isDisplayed())
assertTrue(Message.timestamp.isDisplayed())
} else {
MessageListPage.MessageList.messages.findObjects().forEach {
assertTrue(it.text != text)
}
}
return this
}

fun UserRobot.assertMessageAuthor(isCurrentUser: Boolean): UserRobot {
assertNotEquals(isCurrentUser, Message.authorName.exists())
assertNotEquals(isCurrentUser, Message.avatar.exists())
assertNotEquals(isCurrentUser, Message.authorName.isDisplayed())
assertNotEquals(isCurrentUser, Message.avatar.isDisplayed())
return this
}

fun UserRobot.assertMessageReadStatus(status: MessageReadStatus): UserRobot {
when (status) {
MessageReadStatus.READ -> assertTrue(Message.readStatusIsRead.wait().isDisplayed())
MessageReadStatus.PENDING -> assertTrue(Message.readStatusIsPending.wait().isDisplayed())
MessageReadStatus.SENT -> assertTrue(Message.readStatusIsSent.wait().isDisplayed())
}
return this
}

fun UserRobot.assertMessageFailedIcon(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertTrue(Message.failedIcon.wait().isDisplayed())
} else {
assertFalse(Message.failedIcon.waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertEditedMessage(text: String): UserRobot {
assertMessage(text)
assertEquals(
appContext.getString(R.string.stream_compose_message_list_footnote_edited),
Message.editedLabel.waitToAppear().text
)
return this
}

fun UserRobot.assertMessageSizeChangesAfterEditing(linesCountShouldBeIncreased: Boolean): UserRobot {
val cellHeight = MessageListPage.MessageList.messages.waitToAppear(withIndex = 0).height
val messageText = Message.text.findObject().text
val newLine = "new line"
val newText = if (linesCountShouldBeIncreased) "ok\n${messageText}\n${newLine}" else newLine

editMessage(newText)
assertMessage(newText)

val updatedCellHeight = MessageListPage.MessageList.messages.findObjects().first().height
if (linesCountShouldBeIncreased) {
assertTrue(cellHeight < updatedCellHeight)
} else {
assertTrue(cellHeight > updatedCellHeight)
}
return this
}

fun UserRobot.assertComposerSize(isChangeable: Boolean): UserRobot {
val composer = Composer.inputField
val composerHeight: Int
if (isChangeable) {
composerHeight = composer.findObject().height
val text = "1\n2\n3"
typeText(text)
} else {
val text = "1\n2\n3\n4\n5"
typeText(text)
composerHeight = composer.findObject().height
typeText("${text}\n6")
}
val updatedComposerHeight = composer.findObject().height
assertEquals(composerHeight, updatedComposerHeight)
return this
}

fun UserRobot.assertTypingIndicator(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertEquals(
appContext.resources.getQuantityString(
R.plurals.stream_compose_message_list_header_typing_users,
1,
ParticipantRobot.name
),
MessageListPage.MessageList.typingIndicator.waitToAppear().text
)
} else {
assertFalse(MessageListPage.MessageList.typingIndicator.waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertAttachmentsMenu(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertTrue(MessageListPage.AttachmentPicker.view.waitToAppear().isDisplayed())
} else {
assertFalse(MessageListPage.AttachmentPicker.view.waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertComposerCommandsMenu(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertTrue(Composer.suggestionList.waitToAppear().isDisplayed())
assertTrue(Composer.suggestionListTitle.isDisplayed())
} else {
assertFalse(Composer.suggestionList.waitToDisappear().isDisplayed())
assertFalse(Composer.suggestionListTitle.isDisplayed())
}
return this
}

fun UserRobot.assertComposerMentionsMenu(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertTrue(Composer.participantMentionSuggestion.waitToAppear().isDisplayed())
} else {
assertFalse(Composer.participantMentionSuggestion.waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertScrollToBottomButton(isDisplayed: Boolean): UserRobot {
if (isDisplayed) {
assertTrue(MessageListPage.MessageList.scrollToBottomButton.waitToAppear().isDisplayed())
} else {
assertFalse(MessageListPage.MessageList.scrollToBottomButton.waitToDisappear().isDisplayed())
}
return this
}

Loading

0 comments on commit 15fa235

Please sign in to comment.