Skip to content

Commit 3c5150b

Browse files
[CI] Introduce Backend Tests (#6008)
1 parent 72acf8d commit 3c5150b

5 files changed

Lines changed: 153 additions & 14 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Backend Checks
2+
3+
on:
4+
workflow_dispatch:
5+
6+
env:
7+
BUILD_CACHE_AWS_REGION: ${{ secrets.BUILD_CACHE_AWS_REGION }}
8+
BUILD_CACHE_AWS_BUCKET: ${{ secrets.BUILD_CACHE_AWS_BUCKET }}
9+
BUILD_CACHE_AWS_ACCESS_KEY_ID: ${{ secrets.BUILD_CACHE_AWS_ACCESS_KEY_ID }}
10+
BUILD_CACHE_AWS_SECRET_KEY: ${{ secrets.BUILD_CACHE_AWS_SECRET_KEY }}
11+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12+
13+
jobs:
14+
test-backend-integration:
15+
name: Test Backend Integration
16+
runs-on: ubuntu-24.04
17+
env:
18+
ANDROID_API_LEVEL: 34
19+
steps:
20+
- uses: actions/checkout@v4.2.2
21+
- uses: GetStream/android-ci-actions/actions/setup-java@main
22+
- uses: GetStream/android-ci-actions/actions/enable-kvm@main
23+
- uses: GetStream/android-ci-actions/actions/setup-ruby@main
24+
- name: Run tests
25+
uses: reactivecircus/android-emulator-runner@v2
26+
timeout-minutes: 45
27+
with:
28+
api-level: ${{ env.ANDROID_API_LEVEL }}
29+
disable-animations: true
30+
profile: pixel
31+
arch : x86_64
32+
emulator-options: -no-snapshot-save -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect -camera-back none -camera-front none
33+
script: bundle exec fastlane build_and_run_e2e_test use_backend:true
34+
- name: Upload test results
35+
uses: actions/upload-artifact@v4.4.3
36+
if: failure()
37+
with:
38+
name: logs_${{ env.ANDROID_API_LEVEL }}
39+
path: fastlane/stream-chat-test-mock-server/logs/*

fastlane/Fastfile

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ lane :build_and_run_e2e_test do |options|
6565
batch: options[:batch],
6666
batch_count: options[:batch_count],
6767
local_server: options[:local_server],
68-
mock_server_branch: options[:mock_server_branch]
68+
mock_server_branch: options[:mock_server_branch],
69+
apk_folder_path: "../#{test_flavor}/build/outputs/apk",
70+
use_backend: options[:use_backend]
6971
)
7072
end
7173

@@ -90,7 +92,7 @@ lane :run_e2e_test do |options|
9092
install_test_services
9193
upload_attachments
9294

93-
stream_apk_folder_path = is_ci ? '..' : "../#{test_flavor}/build/outputs/apk"
95+
stream_apk_folder_path = options[:apk_folder_path] || '..'
9496
stream_app_path = "#{stream_apk_folder_path}/e2e/debug/stream-chat-android-compose-sample-e2e-debug.apk"
9597
stream_test_path = "#{stream_apk_folder_path}/androidTest/e2e/debug/stream-chat-android-compose-sample-e2e-debug-androidTest.apk"
9698
sh("adb install -r #{stream_app_path}")
@@ -102,16 +104,24 @@ lane :run_e2e_test do |options|
102104
orchestrator_package_name = 'androidx.test.orchestrator/.AndroidTestOrchestrator'
103105
androidx_test_services_path = sh('adb shell pm path androidx.test.services').strip
104106

105-
run_tests_in_batches = batch_tests(
106-
batch: options[:batch],
107-
batch_count: options[:batch_count],
108-
test_apk_path: stream_test_path
109-
)
107+
backend_test_class = 'io.getstream.chat.android.compose.tests.BackendTests'
108+
109+
if options[:use_backend]
110+
test_filter = "-e class #{backend_test_class}"
111+
else
112+
run_tests_in_batches = batch_tests(
113+
batch: options[:batch],
114+
batch_count: options[:batch_count],
115+
test_apk_path: stream_test_path
116+
)
117+
exclude_backend = "-e notClass #{backend_test_class}"
118+
test_filter = run_tests_in_batches.empty? ? exclude_backend : "#{run_tests_in_batches} #{exclude_backend}"
119+
end
110120

111121
result = sh(
112122
"adb shell 'CLASSPATH=#{androidx_test_services_path}' " \
113123
'app_process / androidx.test.services.shellexecutor.ShellMain am instrument -w -e clearPackageData true ' \
114-
"-e targetInstrumentation #{test_package_name}/#{runner_package_name} #{run_tests_in_batches} #{orchestrator_package_name}"
124+
"-e targetInstrumentation #{test_package_name}/#{runner_package_name} #{test_filter} #{orchestrator_package_name}"
115125
)
116126

117127
sh("adb exec-out sh -c 'cd #{adb_test_results_path} && tar cf - #{allure_results_path}' | tar xvf - -C .") if is_ci

stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/robots/UserRobot.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ class UserRobot {
117117
fun deleteMessage(messageCellIndex: Int = 0, hard: Boolean = false): UserRobot {
118118
openContextMenu(messageCellIndex)
119119
ContextMenu.delete.waitToAppear().click()
120-
ContextMenu.ok.findObject().click()
120+
ContextMenu.ok.waitToAppear().click()
121121
return this
122122
}
123123

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.getstream.chat.android.compose.tests
18+
19+
import io.getstream.chat.android.compose.robots.assertDeletedMessage
20+
import io.getstream.chat.android.compose.robots.assertMessage
21+
import io.getstream.chat.android.compose.robots.assertReaction
22+
import io.getstream.chat.android.compose.sample.ui.InitTestActivity
23+
import io.getstream.chat.android.e2e.test.mockserver.ReactionType
24+
import io.qameta.allure.kotlin.Allure.step
25+
import org.junit.Test
26+
27+
class BackendTests : StreamTestCase() {
28+
29+
override var useMockServer = false
30+
override fun initTestActivity() = InitTestActivity.UserLogin
31+
32+
@Test
33+
fun test_message() {
34+
val originalMessage = "hi"
35+
val editedMessage = "hello"
36+
37+
step("GIVEN user opens a channel") {
38+
userRobot.login().openChannel()
39+
}
40+
step("WHEN user sends a message") {
41+
userRobot.sendMessage(originalMessage)
42+
}
43+
step("THEN message appears") {
44+
userRobot.assertMessage(originalMessage)
45+
}
46+
step("WHEN user edits the message") {
47+
userRobot.editMessage(editedMessage)
48+
}
49+
step("THEN the message is edited") {
50+
userRobot.assertMessage(editedMessage)
51+
}
52+
step("WHEN user deletes the message") {
53+
userRobot.deleteMessage()
54+
}
55+
step("THEN the message is deleted") {
56+
userRobot.assertDeletedMessage()
57+
}
58+
}
59+
60+
@Test
61+
fun test_reaction() {
62+
val message = "test"
63+
64+
step("GIVEN user opens the channel") {
65+
userRobot.login().openChannel()
66+
}
67+
step("WHEN user sends the message") {
68+
userRobot.sendMessage(message)
69+
}
70+
step("AND user adds the reaction") {
71+
userRobot.addReaction(type = ReactionType.LIKE)
72+
}
73+
step("THEN the reaction is added") {
74+
userRobot.assertReaction(type = ReactionType.LIKE, isDisplayed = true)
75+
}
76+
step("WHEN user removes the reaction") {
77+
userRobot.deleteReaction(type = ReactionType.LIKE)
78+
}
79+
step("THEN the reaction is removed") {
80+
userRobot.assertReaction(type = ReactionType.LIKE, isDisplayed = false)
81+
}
82+
}
83+
}

stream-chat-android-compose-sample/src/androidTestE2eDebug/kotlin/io/getstream/chat/android/compose/tests/StreamTestCase.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import org.junit.rules.TestName
4141
abstract class StreamTestCase {
4242

4343
lateinit var mockServer: MockServer
44+
open var useMockServer = true
4445
val userRobot = UserRobot()
4546
lateinit var backendRobot: BackendRobot
4647
lateinit var participantRobot: ParticipantRobot
@@ -53,16 +54,20 @@ abstract class StreamTestCase {
5354

5455
@Before
5556
fun setUp() {
56-
mockServer = MockServer(testName.methodName)
57-
backendRobot = BackendRobot(mockServer)
58-
participantRobot = ParticipantRobot(mockServer)
57+
if (useMockServer) {
58+
mockServer = MockServer(testName.methodName)
59+
backendRobot = BackendRobot(mockServer)
60+
participantRobot = ParticipantRobot(mockServer)
61+
}
5962
startApp()
6063
grantAppPermissions()
6164
}
6265

6366
@After
6467
fun tearDown() {
65-
mockServer.stop()
68+
if (useMockServer) {
69+
mockServer.stop()
70+
}
6671
}
6772

6873
@SuppressLint("InlinedApi")
@@ -84,8 +89,10 @@ abstract class StreamTestCase {
8489
private fun startApp() {
8590
testContext.packageManager.getLaunchIntentForPackage(packageName)?.let {
8691
it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
87-
it.putExtra("BASE_URL", mockServer.url)
8892
it.putExtra("InitTestActivity", initTestActivity())
93+
if (useMockServer) {
94+
it.putExtra("BASE_URL", mockServer.url)
95+
}
8996
testContext.startActivity(it)
9097
} ?: throw IllegalStateException("No launch intent found for package: $packageName")
9198
}

0 commit comments

Comments
 (0)