Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class ChannelListPage {

companion object {
val mute = By.desc("Mute")
val unmute = By.desc("Unmute")
val more = By.desc("More")
}
}
Expand All @@ -60,7 +61,9 @@ class ChannelListPage {

companion object {
val avatar = By.res("Stream_ChannelAvatar")
val name = By.res("Stream_ChannelName")
val name get() = By.res("Stream_ChannelName")

fun name(text: String): BySelector = name.text(text)
val messagePreview = By.res("Stream_MessagePreview")
val deliveryStatusIsRead = Message.deliveryStatusIsRead
val deliveryStatusIsPending = Message.deliveryStatusIsPending
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ open class MessageListPage {
val flag get() = By.res("Stream_ContextMenu_Flag Message")
val pin get() = By.res("Stream_ContextMenu_Pin to this Chat")
val unpin get() = By.res("Stream_ContextMenu_Unpin from this Chat")
val muteUser get() = By.res("Stream_ContextMenu_Mute User")
val unmuteUser get() = By.res("Stream_ContextMenu_Unmute User")
val block get() = By.res("Stream_ContextMenu_Block user")
val unblock get() = By.res("Stream_ContextMenu_Unblock user")
val delete get() = By.res("Stream_ContextMenu_Delete Message")
val showMoreReactions = By.desc("Show more reactions")
val ok = By.text("OK")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.getstream.chat.android.compose.robots

import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import io.getstream.chat.android.compose.pages.ChannelInfoPage
import io.getstream.chat.android.compose.pages.ChannelListPage
import io.getstream.chat.android.compose.pages.CreatePollPage
Expand All @@ -37,6 +38,7 @@ import io.getstream.chat.android.e2e.test.uiautomator.device
import io.getstream.chat.android.e2e.test.uiautomator.findObjects
import io.getstream.chat.android.e2e.test.uiautomator.isDisplayed
import io.getstream.chat.android.e2e.test.uiautomator.longPress
import io.getstream.chat.android.e2e.test.uiautomator.seconds
import io.getstream.chat.android.e2e.test.uiautomator.sleep
import io.getstream.chat.android.e2e.test.uiautomator.swipeDown
import io.getstream.chat.android.e2e.test.uiautomator.swipeUp
Expand Down Expand Up @@ -87,6 +89,11 @@ class UserRobot {
return this
}

fun openChannel(channelName: String): UserRobot {
ChannelListPage.ChannelList.Channel.name(channelName).waitToAppearAndClick()
return this
}

fun openContextMenu(messageCellIndex: Int = 0): UserRobot {
val messages = MessageList.messages.waitToAppearBottomUp()
val message = if (messages.size < messageCellIndex + 1) messages.last() else messages[messageCellIndex]
Expand Down Expand Up @@ -202,6 +209,17 @@ class UserRobot {
return this
}

/**
* Opens the message menu of the message with [text] and waits for [option] to show. The open
* menu rebuilds its options when the own user state lands, so a moderation option that flips
* appears without reopening the menu.
*/
internal fun openContextMenuWithOption(text: String, option: BySelector): UserRobot {
openContextMenu(text)
option.waitDisplayed(timeOutMillis = 15.seconds)
return this
}

fun flagMessage(text: String): UserRobot {
openContextMenu(text)
ContextMenu.flag.waitToAppearAndClick()
Expand All @@ -213,6 +231,30 @@ class UserRobot {
return this
}

fun muteMessageAuthor(text: String): UserRobot {
openContextMenu(text)
ContextMenu.muteUser.waitToAppearAndClick()
return this
}

fun unmuteMessageAuthor(text: String): UserRobot {
openContextMenuWithOption(text, ContextMenu.unmuteUser)
ContextMenu.unmuteUser.waitToAppearAndClick()
return this
}

fun blockMessageAuthor(text: String): UserRobot {
openContextMenu(text)
ContextMenu.block.waitToAppearAndClick()
return this
}

fun unblockMessageAuthor(text: String): UserRobot {
openContextMenuWithOption(text, ContextMenu.unblock)
ContextMenu.unblock.waitToAppearAndClick()
return this
}

fun pinMessage(messageCellIndex: Int = 0): UserRobot {
openContextMenu(messageCellIndex)
ContextMenu.pin.waitToAppearAndClick()
Expand Down Expand Up @@ -402,6 +444,16 @@ class UserRobot {
return this
}

fun tapOnMuteSwipeAction(): UserRobot {
ChannelListPage.ChannelList.SwipeActions.mute.waitToAppearAndClick()
return this
}

fun tapOnUnmuteSwipeAction(): UserRobot {
ChannelListPage.ChannelList.SwipeActions.unmute.waitToAppearAndClick()
return this
}

fun tapOnLeaveGroup(): UserRobot {
ChannelListPage.ChannelMenu.leaveGroup.waitToAppearAndClick()
return this
Expand Down Expand Up @@ -441,7 +493,9 @@ class UserRobot {
return this
}

fun searchForMessage(text: String): UserRobot {
/** Types into the channel list header search input; the app's search mode decides whether
* messages or channels are searched. */
fun search(text: String): UserRobot {
ChannelListPage.Header.searchField.waitToAppear().typeText(text)
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import io.getstream.chat.android.e2e.test.robots.ParticipantRobot
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.e2e.test.uiautomator.isDisplayed
import io.getstream.chat.android.e2e.test.uiautomator.waitDisplayed
import io.getstream.chat.android.e2e.test.uiautomator.waitForCount
import io.getstream.chat.android.e2e.test.uiautomator.waitForText
import io.getstream.chat.android.e2e.test.uiautomator.waitToDisappear
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -80,11 +81,34 @@ fun UserRobot.assertFailedMessageDeliveryStatusInPreview(): UserRobot {
return this
}

fun UserRobot.assertChannelIsMuted(isMuted: Boolean): UserRobot {
if (isMuted) {
assertTrue(Channel.mutedIcon.waitDisplayed())
} else {
assertFalse(Channel.mutedIcon.waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertChannelListIsEmpty(): UserRobot {
assertFalse(ChannelList.channels.waitToDisappear().isDisplayed())
return this
}

fun UserRobot.assertChannelCount(count: Int): UserRobot {
assertEquals(count, ChannelList.channels.waitForCount(count).size)
return this
}

fun UserRobot.assertChannelWithName(name: String, isDisplayed: Boolean = true): UserRobot {
if (isDisplayed) {
assertTrue(Channel.name(name).waitDisplayed())
} else {
assertFalse(Channel.name(name).waitToDisappear().isDisplayed())
}
return this
}

fun UserRobot.assertMessagePreviewTimestamp(isDisplayed: Boolean = true): UserRobot {
if (isDisplayed) {
assertTrue(Channel.timestamp.waitDisplayed())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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.pages.MessageListPage.MessageList.Message.ContextMenu
import io.getstream.chat.android.compose.pages.ThreadPage
import io.getstream.chat.android.e2e.test.mockserver.MessageDeliveryStatus
import io.getstream.chat.android.e2e.test.mockserver.ReactionType
Expand Down Expand Up @@ -552,3 +553,33 @@ fun UserRobot.assertFlagMessageDialog(isDisplayed: Boolean): UserRobot {
assertVisibility(MessageListPage.FlagMessageDialog.body, isDisplayed)
return this
}

/**
* Opens the message menu of the message with [messageText] and asserts the mute option it offers
* for the author. The sample has no muted users list, so the option label is the only place the
* mute state is visible.
*/
fun UserRobot.assertMuteMessageAuthorOption(messageText: String, isAuthorMuted: Boolean): UserRobot {
val expectedOption = if (isAuthorMuted) ContextMenu.unmuteUser else ContextMenu.muteUser
val oppositeOption = if (isAuthorMuted) ContextMenu.muteUser else ContextMenu.unmuteUser
openContextMenuWithOption(messageText, expectedOption)
assertTrue(expectedOption.isDisplayed())
assertFalse(oppositeOption.isDisplayed())
pressBack()
return this
}
Comment thread
gpunto marked this conversation as resolved.

/**
* Opens the message menu of the message with [messageText] and asserts the block option it offers
* for the author. The sample has no blocked users list, so the option label is the only place the
* block state is visible.
*/
fun UserRobot.assertBlockMessageAuthorOption(messageText: String, isAuthorBlocked: Boolean): UserRobot {
val expectedOption = if (isAuthorBlocked) ContextMenu.unblock else ContextMenu.block
val oppositeOption = if (isAuthorBlocked) ContextMenu.block else ContextMenu.unblock
openContextMenuWithOption(messageText, expectedOption)
assertTrue(expectedOption.isDisplayed())
Comment thread
coderabbitai[bot] marked this conversation as resolved.
assertFalse(oppositeOption.isDisplayed())
pressBack()
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.getstream.chat.android.compose.tests

import io.getstream.chat.android.compose.robots.assertChannelActionsSheetForGroupChannel
import io.getstream.chat.android.compose.robots.assertChannelIsMuted
import io.getstream.chat.android.compose.robots.assertChannelListIsEmpty
import io.getstream.chat.android.compose.robots.assertGroupChannelInfoScreen
import io.getstream.chat.android.compose.sample.ui.InitTestActivity
Expand Down Expand Up @@ -110,4 +111,42 @@ class ChannelActionsTests : StreamTestCase() {
userRobot.assertChannelListIsEmpty()
}
}

@AllureId("11560")
@Test
fun test_userMutesChannelFromTheSwipeAction() {
step("GIVEN user logs in") {
userRobot.login().waitForChannelListToLoad()
}
step("WHEN user swipes the channel and taps on the mute action") {
userRobot
.swipeChannel()
.tapOnMuteSwipeAction()
}
step("THEN the channel shows the muted icon") {
userRobot.assertChannelIsMuted(isMuted = true)
}
}

@AllureId("11567")
@Test
fun test_userUnmutesChannelFromTheSwipeAction() {
step("GIVEN user logs in") {
userRobot.login().waitForChannelListToLoad()
}
step("AND user mutes the channel from the swipe action") {
userRobot
.swipeChannel()
.tapOnMuteSwipeAction()
.assertChannelIsMuted(isMuted = true)
}
step("WHEN user swipes the channel and taps on the unmute action") {
userRobot
.swipeChannel()
.tapOnUnmuteSwipeAction()
}
step("THEN the channel shows no muted icon") {
userRobot.assertChannelIsMuted(isMuted = false)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2014-2026 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.chat.android.compose.tests

import io.getstream.chat.android.compose.robots.assertChannelCount
import io.getstream.chat.android.compose.robots.assertChannelWithName
import io.getstream.chat.android.compose.sample.ui.InitTestActivity
import io.qameta.allure.kotlin.Allure.step
import io.qameta.allure.kotlin.AllureId
import org.junit.Test

/**
* Covers the channel search wired into the channel list header. It needs the app started with the
* channel search flag, which configures the channel list with `SearchMode.Channels`. Message
* search, the default mode, is covered by [SearchTests].
*/
class ChannelSearchTests : StreamTestCase() {

override fun initTestActivity() = InitTestActivity.UserLogin
override var useChannelSearch = true

private val channelName = "Blue Team"
private val searchQuery = "Blue"

@AllureId("5934")
@Test
fun test_userSearchesForChannel() {
step("GIVEN channels exist, one with a searchable name") {
backendRobot.generateChannels(channelsCount = 3, channelNames = listOf(channelName))
}
step("AND user logs in") {
userRobot
.login()
.waitForChannelListToLoad()
.assertChannelCount(3)
}
Comment thread
gpunto marked this conversation as resolved.
step("WHEN user searches for the channel by its name") {
userRobot.search(searchQuery)
}
step("THEN only the matching channel is shown") {
userRobot
.assertChannelCount(1)
.assertChannelWithName(channelName)
}
}
}
Loading
Loading