Skip to content

Commit 27ff16c

Browse files
TASK-1828886-1: EmbeddedData repeating view test enhancement
1 parent b819559 commit 27ff16c

File tree

11 files changed

+1665
-1117
lines changed

11 files changed

+1665
-1117
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.pega.PegaVie
2626
import com.pega.constellation.sdk.kmp.samples.basecmpapp.ui.screens.services.ServicesViewModel
2727
import com.pega.constellation.sdk.kmp.test.mock.MockHttpClient
2828
import com.pega.constellation.sdk.kmp.test.mock.PegaVersion
29+
import com.pega.constellation.sdk.kmp.ui.components.cmp.controls.form.internal.AppContext
2930
import kotlinx.coroutines.CoroutineScope
3031
import kotlinx.coroutines.Dispatchers
3132
import okhttp3.OkHttpClient
@@ -49,6 +50,7 @@ abstract class ComposeTest(
4950
fun setUp() {
5051
hideKeyboard()
5152
Injector.init(authManager, engine)
53+
AppContext.init(context)
5254
}
5355

5456
@OptIn(ExperimentalTestApi::class)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import androidx.compose.ui.test.ComposeUiTest
44
import androidx.compose.ui.test.ExperimentalTestApi
55
import androidx.compose.ui.test.assertCountEquals
66
import androidx.compose.ui.test.hasText
7+
import androidx.compose.ui.test.isRoot
8+
import androidx.compose.ui.test.printToLog
79
import androidx.compose.ui.test.waitUntilExactlyOneExists
810
import androidx.compose.ui.test.waitUntilNodeCount
911

@@ -28,3 +30,6 @@ fun ComposeUiTest.waitForNodes(text: String, count: Int, substring: Boolean = fa
2830
onAllNodes(hasText(text, substring)).assertCountEquals(count)
2931
}
3032
}
33+
34+
@OptIn(ExperimentalTestApi::class)
35+
fun ComposeUiTest.printAllFormNodes() = onAllNodes(isRoot())[1].printToLog("FORM NODES")
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,159 @@
11
package com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.cases
22

3+
import androidx.compose.ui.test.ComposeUiTest
34
import androidx.compose.ui.test.ExperimentalTestApi
5+
import androidx.compose.ui.test.SemanticsMatcher
6+
import androidx.compose.ui.test.SemanticsNodeInteractionCollection
7+
import androidx.compose.ui.test.filter
8+
import androidx.compose.ui.test.hasAnyAncestor
49
import androidx.compose.ui.test.hasContentDescription
5-
import androidx.compose.ui.test.hasSetTextAction
10+
import androidx.compose.ui.test.hasTestTag
611
import androidx.compose.ui.test.hasText
12+
import androidx.compose.ui.test.onFirst
713
import androidx.compose.ui.test.onNodeWithContentDescription
814
import androidx.compose.ui.test.onNodeWithText
15+
import androidx.compose.ui.test.onSiblings
916
import androidx.compose.ui.test.performClick
17+
import androidx.compose.ui.test.performScrollTo
1018
import androidx.compose.ui.test.performTextInput
1119
import androidx.compose.ui.test.runComposeUiTest
12-
import androidx.compose.ui.test.waitUntilDoesNotExist
1320
import androidx.compose.ui.test.waitUntilNodeCount
1421
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.ComposeTest
1522
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNode
1623
import com.pega.constellation.sdk.kmp.samples.androidcmpapp.test.waitForNodes
1724
import com.pega.constellation.sdk.kmp.test.mock.PegaVersion
25+
import java.time.LocalDateTime
1826
import kotlin.test.Test
1927

2028
@OptIn(ExperimentalTestApi::class)
21-
class EmbeddedDataTest : ComposeTest(PegaVersion.v24_1_0) {
29+
class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
30+
2231
@Test
23-
fun test_embedded_data() = runComposeUiTest {
24-
setupApp("DIXL-MediaCo-Work-EmbeddedData")
32+
fun test_embedded_data_repeating_view() = runComposeUiTest {
33+
setupApp("O40M3A-MarekCo-Work-EmbeddedDataTest")
2534

2635
// create case
2736
onNodeWithText("New Service").performClick()
2837

2938
// verify form title and instruction
30-
waitForNode("Create EmbeddedData (E-", substring = true)
31-
waitForNode("Embedded Data use-case", substring = true)
32-
waitForNode("EmbeddedData cars editable")
33-
waitForNode("EmbeddedData cars readonly")
39+
waitForNode("ED repeating view editable (", substring = true)
40+
waitForNode("ED repeating view editable & readonly instruction")
41+
42+
// verify repeating views presence
43+
waitForNode("Cars repeating view editable")
44+
waitForNode("Cars repeating view readonly")
3445

3546
// remove and verify empty list
3647
onNodeWithContentDescription("Delete item 1").performClick()
3748
waitForNodes("No items", count = 2)
3849
waitUntilNodeCount(hasContentDescription("No items"), count = 2)
39-
4050
onNodeWithText("Add", substring = true).performClick()
4151

42-
// verify empty record
43-
waitForNodes("Row 1", 2)
44-
waitForNode("Details")
45-
waitForNodes("Brand", 2)
46-
waitForNodes("Model", 2)
47-
waitForNodes("---", 2)
48-
49-
// enter data
50-
onNodeWithText("Client name").performTextInput("Lukasz")
51-
onNode(hasText("Brand") and hasSetTextAction()).performTextInput("Audi")
52-
onNode(hasText("Model") and hasSetTextAction()).performTextInput("A5")
53-
onNodeWithText("Row 1").performClick() // remove focus to propagate data
54-
55-
// verify data propagation
56-
waitForNodes("Lukasz", 2)
57-
waitForNodes("Audi", 2)
58-
waitForNodes("A5", 2)
59-
60-
// adding 2nd record
52+
// verify error banner
53+
onNodeWithText("Next").performClick()
54+
waitForNode("brand: Cannot be blank", substring = true)
55+
56+
// enter data in row 1 and verify
57+
onAllNodes(hasAnyAncestor(hasTestTag("field_group_template_[Cars repeating view editable]"))).let {
58+
it.findFirstWithText("brand").performTextInput("Audi")
59+
it.findFirstWithText("model").performTextInput("A5")
60+
it.findFirstWithText("Price").performTextInput("123000")
61+
it.findFirstWithText("IsFirstOwner").onSiblings().onFirst().performClick()
62+
it.findFirstWithText("interior").performClick()
63+
onNodeWithText("comfort").performClick()
64+
it.findFirstWithText("Insurance").performClick()
65+
onNodeWithText("gold").performClick()
66+
it.findFirstWithText("client meeting date").performClick()
67+
onNodeWithText("Today, ", substring = true).performClick()
68+
onNodeWithText("OK").performClick()
69+
it.findFirstWithText("Client meeting time").performScrollTo().performClick()
70+
onNodeWithText("OK").performClick()
71+
it.findFirstWithText("Transaction date time").performScrollTo().performScrollTo()
72+
.performClick()
73+
onNodeWithText("Today, ", substring = true).performClick()
74+
onNodeWithText("OK").performClick()
75+
onNodeWithText("OK").performClick()
76+
it.findFirstWithText("Notes").performScrollTo().performTextInput("This is a note")
77+
// remove focus to propagate data
78+
onNodeWithText("Cars repeating view readonly").performScrollTo().performClick()
79+
80+
// verify editable data
81+
verifyEmbeddedDataRecord(
82+
nodes = it,
83+
expectedDate = LocalDateTime.now().toString().substring(0, 10),
84+
isEditable = true
85+
)
86+
}
87+
// verify if row 1 data propagated to readonly duplicated view
88+
onAllNodes(hasAnyAncestor(hasTestTag("field_group_template_[Cars repeating view readonly]"))).let {
89+
verifyEmbeddedDataRecord(
90+
nodes = it,
91+
expectedDate = LocalDateTime.now().toString().substring(0, 10),
92+
isEditable = false
93+
)
94+
}
95+
96+
// adding row 2
6197
onNodeWithText("Add", substring = true).performClick()
6298

63-
// enter data in Row 2
64-
waitForNodes("Row 2", count = 2)
65-
onNode(hasText("Client name") and !hasText("Lukasz") and hasSetTextAction()).performTextInput(
66-
"Marek"
67-
)
68-
onNode(hasText("Brand") and !hasText("Audi") and hasSetTextAction()).performTextInput("Ford")
69-
onNode(hasText("Model") and !hasText("A5") and hasSetTextAction()).performTextInput("Focus")
70-
onNodeWithText("Row 2").performClick() // remove focus to propagate data
71-
72-
// verify data propagation for Row 2
73-
waitForNodes("Marek", 2)
74-
waitForNodes("Ford", 2)
75-
waitForNodes("Focus", 2)
76-
77-
// remove Row 1 and verify
78-
onNodeWithContentDescription("Delete item 2").performClick()
79-
80-
waitUntilDoesNotExist(hasText("Row 2"))
81-
waitUntilDoesNotExist(hasText("Marek"))
82-
waitUntilDoesNotExist(hasText("Ford"))
83-
waitUntilDoesNotExist(hasText("Focus"))
84-
85-
// go to next step
99+
// enter data in row 2
100+
waitForNodes("cars 2", count = 2)
101+
onAllNodes(hasAnyAncestor(hasTestTag("field_group_template_[Cars repeating view editable]"))).let { nodes ->
102+
nodes.filter(hasAnyAncestor(hasTestTag("field_group_item_2"))).let {
103+
it.findFirstWithText("brand").performTextInput("Ford")
104+
nodes.findFirstWithText("cars 2").performClick() // remove focus to propagate data
105+
it.findFirstWithText("Ford").assertExists()
106+
}
107+
}
108+
// verify data in row 2 propagated to duplicated
109+
onAllNodes(hasAnyAncestor(hasTestTag("field_group_template_[Cars repeating view readonly]"))).let { nodes ->
110+
nodes.filter(hasAnyAncestor(hasTestTag("field_group_item_2"))).let {
111+
it.findFirstWithText("Ford").assertExists()
112+
}
113+
}
114+
115+
// 2nd step
86116
onNodeWithText("Next").performClick()
117+
waitForNode("ED repeating view readonly (", substring = true)
118+
119+
// verify row 1 on second step
120+
onAllNodes(hasAnyAncestor(hasTestTag("field_group_template_[Cars repeating view readonly]"))).let {
121+
verifyEmbeddedDataRecord(it, expectedDate = "2025-12-16", isEditable = false)
122+
}
123+
}
124+
125+
fun ComposeUiTest.verifyEmbeddedDataRecord(
126+
nodes: SemanticsNodeInteractionCollection,
127+
expectedDate: String,
128+
isEditable: Boolean
129+
) {
130+
with(nodes) {
131+
findFirstWithText("Audi").assertExists()
132+
findFirstWithText("A5").assertExists()
133+
findFirstWithText("123000").assertExists()
134+
if (!isEditable) {
135+
findFirstWithText("Yes").assertExists()
136+
}
137+
findFirstWithText("comfort").assertExists()
138+
findFirstWithText("gold").assertExists()
139+
findFirstWithText(expectedDate).assertExists()
140+
findFirstWithText("12:00 AM").assertExists()
141+
findFirstWithText("$expectedDate 12:00 AM").assertExists()
142+
findFirstWithText("Notes").performScrollTo()
143+
waitUntilAtLeastOneExists(nodes, hasText("This is a note"), timeoutMillis = 5000L)
144+
}
145+
}
87146

88-
// verify form components on 2nd step
89-
waitForNode("Verify EmbeddedData (E-", substring = true)
90-
waitForNode("EmbeddedData cars readonly")
91-
waitForNode("Lukasz")
92-
waitForNode("Details")
93-
waitForNode("Brand")
94-
waitForNode("Audi")
95-
waitForNode("Model")
96-
waitForNode("A5")
147+
fun SemanticsNodeInteractionCollection.findFirstWithText(text: String) =
148+
this.filter(hasText(text)).onFirst()
149+
150+
fun ComposeUiTest.waitUntilAtLeastOneExists(
151+
nodes: SemanticsNodeInteractionCollection,
152+
matcher: SemanticsMatcher,
153+
timeoutMillis: Long = 5000L
154+
) {
155+
waitUntil("exactly 1 nodes match (${matcher.description})", timeoutMillis) {
156+
nodes.filter(matcher).fetchSemanticsNodes().size == 1
157+
}
97158
}
98159
}

0 commit comments

Comments
 (0)