Skip to content

Commit 21cc2ed

Browse files
TASK-1818257: Run compose UI tests on CI
1 parent 3979bab commit 21cc2ed

File tree

7 files changed

+131
-95
lines changed

7 files changed

+131
-95
lines changed

.github/workflows/checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ jobs:
5555
api-level: 35
5656
arch: x86_64
5757
profile: Nexus 6
58-
enable-hw-keyboard: true
58+
enable-hw-keyboard: false
5959
disable-spellchecker: true
60-
script: $GRADLE clean test:check test:connectedAndroidDeviceTest
60+
script: $GRADLE clean :test:connectedAndroidTest :samples:android-cmp-app:connectedAndroidTest
6161

6262
- name: Upload test artifacts for failures
6363
if: failure()

samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/ComposeTest.kt renamed to samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/test/ComposeTest.kt

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.pega.constellation.sdk.kmp.samples.androidcmpapp
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test
22

33
import androidx.compose.foundation.layout.Box
44
import androidx.compose.foundation.layout.fillMaxSize
@@ -18,8 +18,11 @@ import com.pega.constellation.sdk.kmp.core.api.ComponentDefinition
1818
import com.pega.constellation.sdk.kmp.core.api.ComponentManager
1919
import com.pega.constellation.sdk.kmp.core.components.ComponentTypes.Email
2020
import com.pega.constellation.sdk.kmp.engine.webview.android.AndroidWebViewEngine
21-
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.ComposeTest.Mode.MOCK_SERVER
22-
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.ComposeTest.Mode.REAL_SERVER
21+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.AuthInterceptor
22+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest.Mode.MOCK_SERVER
23+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest.Mode.REAL_SERVER
24+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.fake.FakeAuthFlowFactory
25+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.fake.FakeTokenStore
2326
import com.pega.constellation.sdk.kmp.samples.basecmpapp.Injector
2427
import com.pega.constellation.sdk.kmp.samples.basecmpapp.auth.AuthManager
2528
import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.components.CustomEmailComponent
@@ -29,35 +32,21 @@ import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.theme.MediaCoTheme
2932
import com.pega.constellation.sdk.kmp.test.mock.MockHttpClient
3033
import kotlinx.coroutines.CoroutineScope
3134
import kotlinx.coroutines.Dispatchers
32-
import kotlinx.coroutines.launch
3335
import okhttp3.OkHttpClient
3436
import org.publicvalue.multiplatform.oidc.ExperimentalOpenIdConnect
35-
import org.publicvalue.multiplatform.oidc.appsupport.AndroidCodeAuthFlowFactory
36-
import org.publicvalue.multiplatform.oidc.tokenstore.AndroidSettingsTokenStore
3737
import kotlin.test.BeforeTest
3838

3939
@OptIn(ExperimentalOpenIdConnect::class)
4040
abstract class ComposeTest {
41-
enum class Mode { MOCK_SERVER, REAL_SERVER }
41+
// set the mode to switch between mock server and real server testing
42+
private val mode = MOCK_SERVER
4243

4344
private val scope = CoroutineScope(Dispatchers.Default)
44-
private val mode = MOCK_SERVER
4545
private val instrumentation = InstrumentationRegistry.getInstrumentation()
4646
private val context = instrumentation.targetContext
47-
private val authManager = AuthManager(
48-
scope = scope,
49-
authFlowFactory = AndroidCodeAuthFlowFactory(),
50-
tokenStore = AndroidSettingsTokenStore(context).apply {
51-
scope.launch {
52-
saveTokens("YOUR_TOKEN_HERE", null, null)
53-
}
54-
}
55-
)
56-
private val engine = AndroidWebViewEngine(
57-
context = context,
58-
okHttpClient = buildHttpClient(authManager),
59-
nonDxOkHttpClient = buildHttpClient(authManager)
60-
)
47+
private val authManager = AuthManager(scope, FakeAuthFlowFactory(), FakeTokenStore())
48+
private val httpClient = buildHttpClient(authManager)
49+
private val engine = AndroidWebViewEngine(context, httpClient, httpClient)
6150

6251
@BeforeTest
6352
fun setUp() {
@@ -107,6 +96,8 @@ abstract class ComposeTest {
10796
.executeShellCommand("settings put secure show_ime_with_hard_keyboard 0")
10897
}
10998

99+
enum class Mode { MOCK_SERVER, REAL_SERVER }
100+
110101
companion object {
111102
private const val PEGA_URL = "https://insert-url-here.example/prweb"
112103
private const val PEGA_VERSION = "24.1.0"

samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/ComposeTestUtils.kt renamed to samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/test/ComposeTestUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.pega.constellation.sdk.kmp.samples.androidcmpapp
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test
22

33
import androidx.compose.ui.test.ComposeUiTest
44
import androidx.compose.ui.test.ExperimentalTestApi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.cases
2+
3+
import androidx.compose.ui.test.ExperimentalTestApi
4+
import androidx.compose.ui.test.assertTextContains
5+
import androidx.compose.ui.test.onFirst
6+
import androidx.compose.ui.test.onNodeWithText
7+
import androidx.compose.ui.test.onSiblings
8+
import androidx.compose.ui.test.performClick
9+
import androidx.compose.ui.test.performTextInput
10+
import androidx.compose.ui.test.performTextReplacement
11+
import androidx.compose.ui.test.runComposeUiTest
12+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest
13+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNode
14+
import kotlin.test.Test
15+
16+
@OptIn(ExperimentalTestApi::class)
17+
class CaseProcessingTest : ComposeTest() {
18+
@Test
19+
fun test_case_processing_sdk_testing() = runComposeUiTest {
20+
setupApp(caseClassName = "DIXL-MediaCo-Work-SDKTesting")
21+
22+
onNodeWithText("New Service").performClick()
23+
waitForNode("Create")
24+
25+
onNodeWithText("Name", substring = true).performTextInput("Jan")
26+
onNodeWithText("Surname").performTextInput("Kowalski")
27+
onNodeWithText("Url").performTextInput("https://pega.com")
28+
onNodeWithText("Next").performClick()
29+
30+
waitForNode("Submit")
31+
onNodeWithText("Name").assertTextContains("Jan")
32+
onNodeWithText("Surname").assertTextContains("Kowalski")
33+
onNodeWithText("Url").assertTextContains("https://pega.com")
34+
35+
onNodeWithText("Cancel").performClick()
36+
waitForNode("New Service")
37+
}
38+
39+
@Test
40+
fun test_case_processing_service() = runComposeUiTest {
41+
setupApp(caseClassName = "DIXL-MediaCo-Work-NewService")
42+
43+
onNodeWithText("New Service").performClick()
44+
waitForNode("Customer")
45+
46+
onNodeWithText("First Name").performTextInput("Jan")
47+
onNodeWithText("Last Name").performTextInput("Kowalski")
48+
onNodeWithText("Custom Email").performTextInput("invalid email")
49+
50+
onNodeWithText("Service date").performClick()
51+
onNodeWithText("10", substring = true).performClick()
52+
onNodeWithText("OK").performClick()
53+
onNodeWithText("Submit").performClick()
54+
55+
waitForNode("Email: Enter a valid email address")
56+
onNodeWithText("Custom Email").performTextReplacement("jan.kowalski@pega.com")
57+
onNodeWithText("Submit").performClick()
58+
59+
waitForNode("Street")
60+
onNodeWithText("Street").performTextInput("ul. Krakowska 1")
61+
onNodeWithText("City").performTextInput("Kraków")
62+
onNodeWithText("Postal code").performTextInput("31-066")
63+
onNodeWithText("Submit").performClick()
64+
65+
waitForNode("TV Package")
66+
onNodeWithText("TV Package").onSiblings().onFirst().performClick()
67+
onNodeWithText("Submit").performClick()
68+
69+
waitForNode("Other notes")
70+
onNodeWithText("Other notes").performTextInput("Lorem ipsum")
71+
onNodeWithText("Submit").performClick()
72+
73+
waitForNode("New Service")
74+
}
75+
}

samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/CaseProcessingTest.kt renamed to samples/android-cmp-app/src/androidInstrumentedTest/kotlin/com/pega/constellation/sdk/kmp/samples/androidcmpapp/test/cases/EmbeddedDataTest.kt

Lines changed: 7 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,29 @@
1-
package com.pega.constellation.sdk.kmp.samples.androidcmpapp
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.cases
22

33
import androidx.compose.ui.test.ExperimentalTestApi
4-
import androidx.compose.ui.test.assertTextContains
54
import androidx.compose.ui.test.hasContentDescription
65
import androidx.compose.ui.test.hasSetTextAction
76
import androidx.compose.ui.test.hasText
8-
import androidx.compose.ui.test.onFirst
97
import androidx.compose.ui.test.onNodeWithContentDescription
108
import androidx.compose.ui.test.onNodeWithText
11-
import androidx.compose.ui.test.onSiblings
129
import androidx.compose.ui.test.performClick
1310
import androidx.compose.ui.test.performTextInput
14-
import androidx.compose.ui.test.performTextReplacement
1511
import androidx.compose.ui.test.runComposeUiTest
1612
import androidx.compose.ui.test.waitUntilDoesNotExist
1713
import androidx.compose.ui.test.waitUntilNodeCount
14+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest
15+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNode
16+
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNodes
1817
import kotlin.test.Test
1918

2019
@OptIn(ExperimentalTestApi::class)
21-
class CaseProcessingTest : ComposeTest() {
20+
class EmbeddedDataTest : ComposeTest() {
2221
@Test
23-
fun test_case_processing_sdk_testing() = runComposeUiTest {
24-
setupApp(caseClassName = "DIXL-MediaCo-Work-SDKTesting")
25-
26-
onNodeWithText(CREATE_CASE_TEXT).performClick()
27-
waitForNode("Create")
28-
29-
onNodeWithText("Name", substring = true).performTextInput("Jan")
30-
onNodeWithText("Surname").performTextInput("Kowalski")
31-
onNodeWithText("Url").performTextInput("https://pega.com")
32-
onNodeWithText("Next").performClick()
33-
34-
waitForNode("Submit")
35-
onNodeWithText("Name").assertTextContains("Jan")
36-
onNodeWithText("Surname").assertTextContains("Kowalski")
37-
onNodeWithText("Url").assertTextContains("https://pega.com")
38-
39-
onNodeWithText("Cancel").performClick()
40-
waitForNode(CREATE_CASE_TEXT)
41-
}
42-
43-
@Test
44-
fun test_case_processing_service() = runComposeUiTest {
45-
setupApp(caseClassName = "DIXL-MediaCo-Work-NewService")
46-
47-
onNodeWithText(CREATE_CASE_TEXT).performClick()
48-
waitForNode("Customer")
49-
50-
onNodeWithText("First Name").performTextInput("Jan")
51-
onNodeWithText("Last Name").performTextInput("Kowalski")
52-
onNodeWithText("Custom Email").performTextInput("invalid email")
53-
54-
onNodeWithText("Service date").performClick()
55-
onNodeWithText("10", substring = true).performClick()
56-
onNodeWithText("OK").performClick()
57-
onNodeWithText("Submit").performClick()
58-
59-
waitForNode("Email: Enter a valid email address")
60-
onNodeWithText("Custom Email").performTextReplacement("jan.kowalski@pega.com")
61-
onNodeWithText("Submit").performClick()
62-
63-
waitForNode("Street")
64-
onNodeWithText("Street").performTextInput("ul. Krakowska 1")
65-
onNodeWithText("City").performTextInput("Kraków")
66-
onNodeWithText("Postal code").performTextInput("31-066")
67-
onNodeWithText("Submit").performClick()
68-
69-
waitForNode("TV Package")
70-
onNodeWithText("TV Package").onSiblings().onFirst().performClick()
71-
onNodeWithText("Submit").performClick()
72-
73-
waitForNode("Other notes")
74-
onNodeWithText("Other notes").performTextInput("Lorem ipsum")
75-
onNodeWithText("Submit").performClick()
76-
77-
waitForNode(CREATE_CASE_TEXT)
78-
}
79-
80-
@Test
81-
fun test_case_processing_embedded_data() = runComposeUiTest {
22+
fun test_embedded_data() = runComposeUiTest {
8223
setupApp("DIXL-MediaCo-Work-EmbeddedData")
8324

8425
// create case
85-
onNodeWithText(CREATE_CASE_TEXT).performClick()
26+
onNodeWithText("New Service").performClick()
8627

8728
// verify form title and instruction
8829
waitForNode("Create EmbeddedData (E-")
@@ -151,8 +92,4 @@ class CaseProcessingTest : ComposeTest() {
15192
waitForNode("Model")
15293
waitForNode("A5")
15394
}
154-
155-
companion object {
156-
private const val CREATE_CASE_TEXT = "New Service"
157-
}
15895
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.fake
2+
3+
import org.publicvalue.multiplatform.oidc.OpenIdConnectClient
4+
import org.publicvalue.multiplatform.oidc.appsupport.CodeAuthFlowFactory
5+
6+
class FakeAuthFlowFactory : CodeAuthFlowFactory {
7+
override fun createAuthFlow(client: OpenIdConnectClient) = error("Not supported in tests")
8+
override fun createEndSessionFlow(client: OpenIdConnectClient) = error("Not supported in tests")
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.fake
2+
3+
import kotlinx.coroutines.flow.flowOf
4+
import org.publicvalue.multiplatform.oidc.ExperimentalOpenIdConnect
5+
import org.publicvalue.multiplatform.oidc.tokenstore.TokenStore
6+
7+
@OptIn(ExperimentalOpenIdConnect::class)
8+
class FakeTokenStore : TokenStore() {
9+
override val accessTokenFlow = flowOf(TEST_ACCESS_TOKEN)
10+
override val refreshTokenFlow = flowOf(null)
11+
override val idTokenFlow = flowOf(null)
12+
13+
override suspend fun getAccessToken() = TEST_ACCESS_TOKEN
14+
override suspend fun getRefreshToken() = null
15+
override suspend fun getIdToken() = null
16+
override suspend fun removeAccessToken() {}
17+
override suspend fun removeRefreshToken() {}
18+
override suspend fun removeIdToken() {}
19+
override suspend fun saveTokens(accessToken: String, refreshToken: String?, idToken: String?) {}
20+
21+
companion object Companion {
22+
const val TEST_ACCESS_TOKEN = "TEST_ACCESS_TOKEN"
23+
}
24+
}

0 commit comments

Comments
 (0)