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
@@ -0,0 +1,34 @@
/*
* 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.pages

import androidx.test.uiautomator.By
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.ui.common.R as UiCommonR

/**
* The channel info screen. Its rows carry no test tags, so they are matched by the option label.
*/
class ChannelInfoPage {

companion object {
val groupTitle get() = By.text(appContext.getString(UiCommonR.string.stream_ui_channel_info_group_title))

val pinnedMessagesOption
get() = By.text(appContext.getString(UiCommonR.string.stream_ui_channel_info_option_pinned_messages))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package io.getstream.chat.android.compose.pages
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import io.getstream.chat.android.compose.pages.MessageListPage.MessageList.Message
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.compose.R as ComposeR
import io.getstream.chat.android.compose.sample.R as SampleR

class ChannelListPage {

Expand All @@ -31,6 +34,13 @@ class ChannelListPage {
}
}

class NavigationDrawer {

companion object {
val reminders get() = By.text(appContext.getString(SampleR.string.navigation_drawer_later))
}
}

class ChannelList {

companion object {
Expand Down Expand Up @@ -72,6 +82,10 @@ class ChannelListPage {
val viewInfo = By.res("Stream_ContextMenu_View info")
val leaveGroup = By.res("Stream_ContextMenu_Leave group")
val deleteGroup = By.res("Stream_ContextMenu_Delete Group")

// Leave and delete open a confirmation dialog whose confirm button is the generic
// dialog one, not the action label.
val confirmButton get() = By.text(appContext.getString(ComposeR.string.stream_compose_ok))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ package io.getstream.chat.android.compose.pages
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import io.getstream.chat.android.e2e.test.mockserver.ReactionType
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import java.util.regex.Pattern
import io.getstream.chat.android.compose.R as ComposeR

open class MessageListPage {

Expand Down Expand Up @@ -83,6 +85,15 @@ open class MessageListPage {
}
}

class FlagMessageDialog {

companion object {
// The dialog title repeats the message option label, so the body text is what
// identifies the dialog.
val body get() = By.text(appContext.getString(ComposeR.string.stream_compose_flag_message_text))
}
}

class MessageList {

companion object {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.pages

import androidx.test.uiautomator.By
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.compose.R as ComposeR
import io.getstream.chat.android.compose.sample.R as SampleR

/**
* The pinned messages screen. Its rows carry no test tags, so a pinned message is matched by the
* message text shown in the row preview.
*/
class PinnedMessagesPage {

companion object {
val title get() = By.text(appContext.getString(SampleR.string.pinned_messages_title))

val emptyTitle
get() = By.text(appContext.getString(ComposeR.string.stream_compose_pinned_message_list_empty_title))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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.pages

import androidx.test.uiautomator.By
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.compose.sample.R as SampleR

/**
* The message reminders screen, opened from the navigation drawer. Its rows carry no test tags, so
* a reminder is matched by the message text and the status label.
*/
class RemindersPage {

companion object {
val title get() = By.text(appContext.getString(SampleR.string.reminders_title))

val emptyTitle get() = By.text(appContext.getString(SampleR.string.reminders_no_results))

val savedForLaterStatus
get() = By.text(appContext.getString(SampleR.string.reminders_status_save_for_later))

/** The reminder row of the message with [messageText]. */
fun reminder(messageText: String) =
By.res("Stream_MessageReminderItem").hasDescendant(By.text(messageText))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.pages

import androidx.test.uiautomator.By
import io.getstream.chat.android.e2e.test.uiautomator.appContext
import io.getstream.chat.android.e2e.test.uiautomator.resources
import io.getstream.chat.android.compose.R as ComposeR

/**
* The threads list screen, opened from the threads tab of the sample bottom bar. Its rows carry no
* test tags, so a thread is matched by the parent message preview and the reply count label.
*/
class ThreadListPage {

companion object {
val threadsTab = By.res("Stream_BottomBarThreadsTab")

val parentMessagePreview = By.res("Stream_MessagePreview")

val emptyTitle
get() = By.text(appContext.getString(ComposeR.string.stream_compose_thread_list_empty_title))

fun repliesCountLabel(count: Int) = By.text(
resources.getQuantityString(ComposeR.plurals.stream_compose_thread_list_item_reply_count, count, count),
)
}
}
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 io.getstream.chat.android.compose.pages.ChannelInfoPage
import io.getstream.chat.android.compose.pages.ChannelListPage
import io.getstream.chat.android.compose.pages.LoginPage
import io.getstream.chat.android.compose.pages.MessageListPage
Expand All @@ -25,6 +26,7 @@ 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.ThreadListPage
import io.getstream.chat.android.compose.pages.ThreadPage
import io.getstream.chat.android.e2e.test.mockserver.AttachmentType
import io.getstream.chat.android.e2e.test.mockserver.ReactionType
Expand Down Expand Up @@ -57,11 +59,18 @@ class UserRobot {
return this
}

fun logout(): UserRobot {
/** Opens the navigation drawer, which the channel list header avatar reveals. */
fun openNavigationDrawer(): UserRobot {
ChannelListPage.Header.userAvatar.waitToAppearAndClick()
return this
}

fun openReminders(): UserRobot {
openNavigationDrawer()
ChannelListPage.NavigationDrawer.reminders.waitToAppearAndClick()
return this
}

fun waitForChannelListToLoad(): UserRobot {
ChannelListPage.ChannelList.channels.wait()
return this
Expand Down Expand Up @@ -192,6 +201,17 @@ class UserRobot {
return this
}

fun flagMessage(text: String): UserRobot {
openContextMenu(text)
ContextMenu.flag.waitToAppearAndClick()
return this
}

fun confirmFlagMessage(): UserRobot {
ContextMenu.ok.waitToAppearAndClick()
return this
}

fun pinMessage(messageCellIndex: Int = 0): UserRobot {
openContextMenu(messageCellIndex)
ContextMenu.pin.waitToAppearAndClick()
Expand Down Expand Up @@ -381,11 +401,59 @@ class UserRobot {
return this
}

fun tapOnLeaveGroup(): UserRobot {
ChannelListPage.ChannelMenu.leaveGroup.waitToAppearAndClick()
return this
}

fun tapOnDeleteGroup(): UserRobot {
ChannelListPage.ChannelMenu.deleteGroup.waitToAppearAndClick()
return this
}

fun confirmChannelAction(): UserRobot {
ChannelListPage.ChannelMenu.confirmButton.waitToAppearAndClick()
return this
}

fun tapOnViewChannelInfo(): UserRobot {
ChannelListPage.ChannelMenu.viewInfo.waitToAppearAndClick()
return this
}

fun tapOnPinnedMessagesOption(): UserRobot {
ChannelInfoPage.pinnedMessagesOption.waitToAppearAndClick()
return this
}

fun openThreadList(): UserRobot {
ThreadListPage.threadsTab.waitToAppearAndClick()
return this
}

/**
* Taps the first thread of the thread list. The rows carry no test tag of their own, so the tap
* lands on the parent message preview inside the row, which the row handles.
*/
fun openThreadFromThreadList(): UserRobot {
ThreadListPage.parentMessagePreview.waitToAppearAndClick()
return this
}

fun searchForMessage(text: String): UserRobot {
ChannelListPage.Header.searchField.waitToAppear().typeText(text)
return this
}

/**
* Taps the first search result. The result rows carry no test tag of their own, so the tap
* lands on the message preview inside the row, which the row handles.
*/
fun tapOnSearchResult(): UserRobot {
ChannelListPage.ChannelList.Channel.messagePreview.waitToAppearAndClick()
return this
}

/**
* Scrolls the message list up one page at a time until the message with [messageText] is
* displayed and clear of the top edge of the list, giving up after [maxScrolls] pages. The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package io.getstream.chat.android.compose.robots

import androidx.test.uiautomator.By
import io.getstream.chat.android.compose.pages.ChannelInfoPage
import io.getstream.chat.android.compose.pages.ChannelListPage.ChannelList
import io.getstream.chat.android.compose.pages.ChannelListPage.ChannelList.Channel
import io.getstream.chat.android.compose.pages.ChannelListPage.ChannelMenu
import io.getstream.chat.android.e2e.test.robots.ParticipantRobot
Expand All @@ -29,7 +31,6 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import io.getstream.chat.android.compose.R as ComposeR
import io.getstream.chat.android.ui.common.R as UiCommonR

fun UserRobot.assertChannelAvatar(): UserRobot {
assertTrue(Channel.avatar.isDisplayed())
Expand All @@ -56,10 +57,8 @@ fun UserRobot.assertChannelActionsSheetForGroupChannel(): UserRobot {
}

fun UserRobot.assertGroupChannelInfoScreen(): UserRobot {
assertTrue(By.text(appContext.getString(UiCommonR.string.stream_ui_channel_info_group_title)).waitDisplayed())
assertTrue(
By.text(appContext.getString(UiCommonR.string.stream_ui_channel_info_option_pinned_messages)).isDisplayed(),
)
assertTrue(ChannelInfoPage.groupTitle.waitDisplayed())
assertTrue(ChannelInfoPage.pinnedMessagesOption.isDisplayed())
return this
}

Expand All @@ -81,6 +80,11 @@ fun UserRobot.assertFailedMessageDeliveryStatusInPreview(): UserRobot {
return this
}

fun UserRobot.assertChannelListIsEmpty(): UserRobot {
assertFalse(ChannelList.channels.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 @@ -542,3 +542,8 @@ fun UserRobot.assertLinkPreviewInComposer(isDisplayed: Boolean): UserRobot {
}
return this
}

fun UserRobot.assertFlagMessageDialog(isDisplayed: Boolean): UserRobot {
assertVisibility(MessageListPage.FlagMessageDialog.body, isDisplayed)
return this
}
Loading
Loading