Skip to content

Commit b6789e8

Browse files
[CI] Implement Lobby E2E tests (#1358)
1 parent 696e7b3 commit b6789e8

File tree

14 files changed

+339
-79
lines changed

14 files changed

+339
-79
lines changed

.github/workflows/e2e-test-cron.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ jobs:
3939
fail-fast: false
4040
env:
4141
ANDROID_API_LEVEL: ${{ matrix.android_api_level }}
42+
STREAM_SDK_TEST_APP: ${{ vars.STREAM_SDK_TEST_APP }}
4243
steps:
4344
- uses: actions/checkout@v4.2.2
4445
- uses: actions/download-artifact@v4.1.8

.github/workflows/e2e-test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
env:
5252
ANDROID_API_LEVEL: 34
5353
LAUNCH_ID: ${{ needs.allure_testops_launch.outputs.launch_id }}
54+
STREAM_SDK_TEST_APP: ${{ vars.STREAM_SDK_TEST_APP }}
5455
steps:
5556
- uses: actions/checkout@v4.2.2
5657
- uses: actions/download-artifact@v4.1.8

demo-app/src/androidTestE2etestingDebug/kotlin/io/getstream/video/android/pages/CallPage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class CallPage {
6666
val cameraDisabled = By.res("Stream_ParticipantsListUserCamera_Enabled_false")
6767
val microphoneEnabled = By.res("Stream_ParticipantsListUserMicrophone_Enabled_true")
6868
val microphoneDisabled = By.res("Stream_ParticipantsListUserMicrophone_Enabled_false")
69-
val closeButton = LobbyPage.closeButton
69+
val closeButton = By.res("Stream_ParticipantsListCloseButton")
7070
}
7171
}
7272

demo-app/src/androidTestE2etestingDebug/kotlin/io/getstream/video/android/pages/LobbyPage.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import androidx.test.uiautomator.BySelector
2222
class LobbyPage {
2323

2424
companion object {
25-
val closeButton = By.res("Stream_CloseButton")
25+
val closeButton = By.res("Stream_LobbyCloseButton")
2626
val cameraEnabledToggle = By.res("Stream_CameraToggle_Enabled_true")
2727
val cameraDisabledToggle = By.res("Stream_CameraToggle_Enabled_false")
2828
val microphoneEnabledToggle = By.res("Stream_MicrophoneToggle_Enabled_true")

demo-app/src/androidTestE2etestingDebug/kotlin/io/getstream/video/android/robots/UserRobot.kt

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ import io.getstream.video.android.uiautomator.defaultTimeout
2727
import io.getstream.video.android.uiautomator.device
2828
import io.getstream.video.android.uiautomator.findObject
2929
import io.getstream.video.android.uiautomator.findObjects
30+
import io.getstream.video.android.uiautomator.seconds
3031
import io.getstream.video.android.uiautomator.typeText
32+
import io.getstream.video.android.uiautomator.waitForText
3133
import io.getstream.video.android.uiautomator.waitToAppear
3234

3335
class UserRobot {
@@ -55,9 +57,20 @@ class UserRobot {
5557
return this
5658
}
5759

58-
fun joinCall(callId: String? = null, camera: Boolean = true, mic: Boolean = true): UserRobot {
60+
fun joinCall(
61+
callId: String? = null,
62+
camera: UserControls? = null,
63+
microphone: UserControls? = null,
64+
): UserRobot {
5965
enterLobby(callId)
60-
joinCallFromLobby(camera = camera, mic = mic)
66+
if (camera != null) {
67+
camera(camera, hard = true)
68+
}
69+
if (microphone != null) {
70+
microphone(microphone, hard = true)
71+
}
72+
joinCallFromLobby()
73+
waitForCallToStart()
6174
return this
6275
}
6376

@@ -66,29 +79,20 @@ class UserRobot {
6679
CallDetailsPage.callIdInputField.waitToAppear().typeText(callId)
6780
}
6881
CallDetailsPage.joinCallButton.waitToAppear().click()
82+
waitForLobbyToOpen()
6983
return this
7084
}
7185

7286
// LobbyPage actions
7387

74-
fun joinCallFromLobby(camera: Boolean = true, mic: Boolean = true): UserRobot {
75-
LobbyPage.joinCallButton.waitToAppear()
76-
val cameraIsEnabled = LobbyPage.cameraEnabledToggle.findObjects().isNotEmpty()
77-
val micIsEnabled = LobbyPage.microphoneEnabledToggle.findObjects().isNotEmpty()
78-
79-
if (camera && !cameraIsEnabled) {
80-
LobbyPage.cameraDisabledToggle.findObject().click()
81-
} else if (!camera && cameraIsEnabled) {
82-
LobbyPage.cameraEnabledToggle.findObject().click()
83-
}
84-
85-
if (mic && !micIsEnabled) {
86-
LobbyPage.microphoneDisabledToggle.findObject().click()
87-
} else if (!mic && micIsEnabled) {
88-
LobbyPage.microphoneEnabledToggle.findObject().click()
89-
}
90-
88+
fun joinCallFromLobby(): UserRobot {
9189
LobbyPage.joinCallButton.findObject().click()
90+
waitForCallToStart()
91+
return this
92+
}
93+
94+
private fun waitForLobbyToOpen(): UserRobot {
95+
LobbyPage.closeButton.waitToAppear()
9296
return this
9397
}
9498

@@ -107,34 +111,79 @@ class UserRobot {
107111
return this
108112
}
109113

110-
fun camera(action: UserControls): UserRobot {
111-
CallPage.callInfoView.waitToAppear()
114+
fun camera(
115+
action: UserControls,
116+
hard: Boolean = false, // When true, hard-toggles to ensure server state syncs properly
117+
): UserRobot {
112118
val isEnabled = CallPage.cameraEnabledToggle.findObjects().isNotEmpty()
113119

114-
if (action == UserControls.ENABLE && !isEnabled) {
115-
CallPage.cameraDisabledToggle.findObject().click()
116-
} else if (action == UserControls.DISABLE && isEnabled) {
117-
CallPage.cameraEnabledToggle.findObject().click()
120+
when {
121+
hard -> when (action) {
122+
UserControls.ENABLE -> {
123+
if (isEnabled) {
124+
CallPage.cameraEnabledToggle.findObject().click()
125+
CallPage.cameraDisabledToggle.waitToAppear().click()
126+
} else {
127+
CallPage.cameraDisabledToggle.findObject().click()
128+
}
129+
}
130+
UserControls.DISABLE -> {
131+
if (isEnabled) {
132+
CallPage.cameraEnabledToggle.findObject().click()
133+
} else {
134+
CallPage.cameraDisabledToggle.findObject().click()
135+
CallPage.cameraEnabledToggle.waitToAppear().click()
136+
}
137+
}
138+
}
139+
action == UserControls.ENABLE && !isEnabled -> {
140+
CallPage.cameraDisabledToggle.findObject().click()
141+
}
142+
action == UserControls.DISABLE && isEnabled -> {
143+
CallPage.cameraEnabledToggle.findObject().click()
144+
}
118145
}
119146

120147
return this
121148
}
122149

123-
fun microphone(action: UserControls): UserRobot {
124-
CallPage.callInfoView.waitToAppear()
150+
fun microphone(
151+
action: UserControls,
152+
hard: Boolean = false, // When true, hard-toggles to ensure server state syncs properly
153+
): UserRobot {
125154
val isEnabled = CallPage.microphoneEnabledToggle.findObjects().isNotEmpty()
126155

127-
if (action == UserControls.ENABLE && !isEnabled) {
128-
CallPage.microphoneDisabledToggle.findObject().click()
129-
} else if (action == UserControls.DISABLE && isEnabled) {
130-
CallPage.microphoneEnabledToggle.findObject().click()
156+
when {
157+
hard -> when (action) {
158+
UserControls.ENABLE -> {
159+
if (isEnabled) {
160+
CallPage.microphoneEnabledToggle.findObject().click()
161+
CallPage.microphoneDisabledToggle.waitToAppear().click()
162+
} else {
163+
CallPage.microphoneDisabledToggle.findObject().click()
164+
}
165+
}
166+
UserControls.DISABLE -> {
167+
if (isEnabled) {
168+
CallPage.microphoneEnabledToggle.findObject().click()
169+
} else {
170+
CallPage.microphoneDisabledToggle.findObject().click()
171+
CallPage.microphoneEnabledToggle.waitToAppear().click()
172+
}
173+
}
174+
}
175+
action == UserControls.ENABLE && !isEnabled -> {
176+
CallPage.microphoneDisabledToggle.findObject().click()
177+
}
178+
action == UserControls.DISABLE && isEnabled -> {
179+
CallPage.microphoneEnabledToggle.findObject().click()
180+
}
131181
}
132182

133183
return this
134184
}
135185

136186
fun settings(action: UserControls): UserRobot {
137-
CallPage.callInfoView.waitToAppear()
138187
val isEnabled = CallPage.callSettingsOpenToggle.findObjects().isNotEmpty()
139188

140189
if (action == UserControls.ENABLE && !isEnabled) {
@@ -175,6 +224,20 @@ class UserRobot {
175224
CallPage.cornerDraggableView.waitToAppear().swipe(direction, 1.0f)
176225
return this
177226
}
227+
228+
fun waitForParticipantsToJoin(count: Int, timeOutMillis: Long = 20.seconds): UserRobot {
229+
val user = 1
230+
val participants = user + count
231+
CallPage.participantsCountBadge
232+
.waitToAppear()
233+
.waitForText(expectedText = participants.toString(), timeOutMillis = timeOutMillis)
234+
return this
235+
}
236+
237+
private fun waitForCallToStart(): UserRobot {
238+
CallPage.callInfoView.waitToAppear()
239+
return this
240+
}
178241
}
179242

180243
enum class UserControls {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2014-2024 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-video-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.video.android.robots
18+
19+
import io.getstream.video.android.pages.CallPage
20+
import io.getstream.video.android.uiautomator.isDisplayed
21+
import io.getstream.video.android.uiautomator.waitForText
22+
import io.getstream.video.android.uiautomator.waitToAppear
23+
import org.junit.Assert.assertTrue
24+
25+
fun UserRobot.assertCallControls(microphone: Boolean, camera: Boolean): UserRobot {
26+
assertTrue(CallPage.callSettingsClosedToggle.waitToAppear().isDisplayed())
27+
assertTrue(CallPage.hangUpButton.isDisplayed())
28+
assertTrue(CallPage.chatButton.isDisplayed())
29+
assertTrue(CallPage.cameraPositionToggleFront.isDisplayed())
30+
31+
if (microphone) {
32+
assertTrue(CallPage.microphoneEnabledToggle.isDisplayed())
33+
} else {
34+
assertTrue(CallPage.microphoneDisabledToggle.isDisplayed())
35+
}
36+
37+
if (camera) {
38+
assertTrue(CallPage.cameraEnabledToggle.isDisplayed())
39+
} else {
40+
assertTrue(CallPage.cameraDisabledToggle.isDisplayed())
41+
}
42+
43+
return this
44+
}
45+
46+
fun UserRobot.assertParticipantsCountOnCall(count: Int): UserRobot {
47+
val user = 1
48+
val participants = user + count
49+
CallPage.participantsCountBadge
50+
.waitToAppear()
51+
.waitForText(expectedText = participants.toString())
52+
return this
53+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2014-2024 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-video-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.video.android.robots
18+
19+
import io.getstream.video.android.pages.LobbyPage
20+
import io.getstream.video.android.uiautomator.isDisplayed
21+
import io.getstream.video.android.uiautomator.waitToAppear
22+
import org.junit.Assert.assertTrue
23+
24+
fun UserRobot.assertLobby(microphone: Boolean, camera: Boolean): UserRobot {
25+
assertTrue(LobbyPage.closeButton.waitToAppear().isDisplayed())
26+
assertTrue(LobbyPage.joinCallButton.isDisplayed())
27+
28+
if (microphone) {
29+
assertTrue(LobbyPage.microphoneEnabledToggle.waitToAppear().isDisplayed())
30+
assertTrue(LobbyPage.microphoneEnabledIcon.isDisplayed())
31+
} else {
32+
assertTrue(LobbyPage.microphoneDisabledToggle.waitToAppear().isDisplayed())
33+
assertTrue(LobbyPage.microphoneDisabledIcon.isDisplayed())
34+
}
35+
36+
if (camera) {
37+
assertTrue(LobbyPage.cameraEnabledToggle.waitToAppear().isDisplayed())
38+
assertTrue(LobbyPage.cameraEnabledView.isDisplayed())
39+
} else {
40+
assertTrue(LobbyPage.cameraDisabledToggle.waitToAppear().isDisplayed())
41+
assertTrue(LobbyPage.cameraDisabledView.isDisplayed())
42+
}
43+
44+
return this
45+
}
46+
47+
fun UserRobot.assertParticipantsCountInLobby(count: Int): UserRobot {
48+
assertTrue(LobbyPage.callParticipantsCount(count).waitToAppear().isDisplayed())
49+
return this
50+
}

0 commit comments

Comments
 (0)