@@ -4,6 +4,7 @@ import androidx.compose.ui.test.ComposeUiTest
44import androidx.compose.ui.test.ExperimentalTestApi
55import androidx.compose.ui.test.SemanticsMatcher
66import androidx.compose.ui.test.SemanticsNodeInteractionCollection
7+ import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
78import androidx.compose.ui.test.filter
89import androidx.compose.ui.test.hasAnyAncestor
910import androidx.compose.ui.test.hasClickAction
@@ -62,26 +63,26 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
6263
6364 // enter data in row 1 and verify
6465 val carsEditableFieldGroup = " field_group_template_[Cars repeating view editable]"
65- allDescendantsOf (carsEditableFieldGroup).let {
66- it.findFirstWithText (" brand" ).performTextInput(" Audi" )
67- it.findFirstWithText (" model" ).performTextInput(" A5" )
68- it.findFirstWithText (" Price" ).performTextInput(" 123000" )
69- it.findFirstWithText (" IsFirstOwner" ).onSiblings().onFirst().performClick()
70- it.findFirstWithText (" interior" ).performScrollTo().performClick()
66+ onAllDescendantsOf (carsEditableFieldGroup).let {
67+ it.find (" brand" ).performTextInput(" Audi" )
68+ it.find (" model" ).performTextInput(" A5" )
69+ it.find (" Price" ).performTextInput(" 123000" )
70+ it.find (" IsFirstOwner" ).onSiblings().onFirst().performClick()
71+ it.find (" interior" ).performScrollTo().performClick()
7172 onNodeWithText(" comfort" ).performClick()
72- it.findFirstWithText (" Insurance" ).performScrollTo().performClick()
73+ it.find (" Insurance" ).performScrollTo().performClick()
7374 onNodeWithText(" gold" ).performClick()
74- it.findFirstWithText (" client meeting date" ).performScrollTo().performClick()
75+ it.find (" client meeting date" ).performScrollTo().performClick()
7576 onNodeWithText(" Today, " , substring = true ).performClick()
7677 onNodeWithText(" OK" ).performClick()
77- it.findFirstWithText (" Client meeting time" ).performScrollTo().performClick()
78+ it.find (" Client meeting time" ).performScrollTo().performClick()
7879 onNodeWithText(" OK" ).performClick()
79- it.findFirstWithText (" Transaction date time" ).performScrollTo().performScrollTo()
80+ it.find (" Transaction date time" ).performScrollTo().performScrollTo()
8081 .performClick()
8182 onNodeWithText(" Today, " , substring = true ).performClick()
8283 onNodeWithText(" OK" ).performClick()
8384 onNodeWithText(" OK" ).performClick()
84- it.findFirstWithText (" Notes" ).performScrollTo().performTextInput(" This is a note" )
85+ it.find (" Notes" ).performScrollTo().performTextInput(" This is a note" )
8586 // remove focus to propagate data
8687 onNodeWithText(" Cars repeating view readonly" ).performScrollTo().performClick()
8788
@@ -95,7 +96,7 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
9596 // verify if row 1 data propagated to readonly duplicated view
9697 val carsReadonlyFieldGroup = " field_group_template_[Cars repeating view readonly]"
9798 verifyFieldGroupItem(
98- nodes = allDescendantsOf (carsReadonlyFieldGroup),
99+ nodes = onAllDescendantsOf (carsReadonlyFieldGroup),
99100 expectedDate = LocalDateTime .now().toString().substring(0 , 10 ),
100101 isEditable = false
101102 )
@@ -104,16 +105,16 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
104105 onNodeWithText(" Add" , substring = true ).performScrollTo().performClick()
105106 // enter data in row 2
106107 waitForNodes(" cars 2" , count = 2 )
107- allDescendantsOf (carsEditableFieldGroup).let { nodes ->
108+ onAllDescendantsOf (carsEditableFieldGroup).let { nodes ->
108109 nodes.filterDescendantsOf(" field_group_item_2" ).let {
109- it.findFirstWithText (" brand" ).performTextInput(" Ford" )
110- nodes.findFirstWithText (" cars 2" ).performClick() // remove focus to propagate data
111- it.findFirstWithText (" Ford" ).assertExists()
110+ it.find (" brand" ).performTextInput(" Ford" )
111+ nodes.find (" cars 2" ).performClick() // remove focus to propagate data
112+ it.find (" Ford" ).assertExists()
112113 }
113114 }
114115 // verify data in row 2 propagated to duplicated
115- allDescendantsOf (carsReadonlyFieldGroup).filterDescendantsOf(" field_group_item_2" ).let {
116- waitUntilAtLeastOneExists (it, hasText(" Ford" ), timeoutMillis = 5000L )
116+ onAllDescendantsOf (carsReadonlyFieldGroup).filterDescendantsOf(" field_group_item_2" ).let {
117+ waitUntilExactlyOneExists (it, hasText(" Ford" ), timeoutMillis = 5000L )
117118 }
118119
119120 // 2nd step
@@ -122,7 +123,7 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
122123
123124 // verify row 1 on second step
124125 verifyFieldGroupItem(
125- allDescendantsOf (carsReadonlyFieldGroup),
126+ onAllDescendantsOf (carsReadonlyFieldGroup),
126127 expectedDate = " 2025-12-16" ,
127128 isEditable = false
128129 )
@@ -155,9 +156,7 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
155156 // verify table title
156157 waitForNode(" Cars editable table" )
157158 // verify columns
158- columnValues.keys.forEach {
159- waitForNodes(it.uppercase(), count = 2 )
160- } // despite there is only one table with column names, test sees two of them
159+ columnValues.keys.forEach { waitForTableNode(it.uppercase()) }
161160
162161 // verify add and delete records
163162 onNodeWithText(" + Add cars" ).performClick()
@@ -195,44 +194,44 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
195194 // verify table title
196195 waitForNode(" Cars editable table with popup" )
197196 // verify columns
198- columnValues.keys.forEach { waitForNodes (it.uppercase(), count = 2 ) } // despite there is only one table with column names, test sees two of them
197+ columnValues.keys.forEach { waitForTableNode (it.uppercase()) }
199198 // verify table data
200- columnValues.values.forEach { waitForNodes (it, count = 2 ) }
199+ columnValues.values.forEach { waitForTableNode (it) }
201200 // verify reorder icon exists
202201 waitUntilAtLeastOneExists(hasContentDescription(" Reorder item 1" ))
203202 // verify edit record popup
204203 onAllNodes(hasContentDescription(" Edit item 1" )).onFirst().performScrollTo().performClick()
205204 waitForNode(" Edit Record" )
206205
207- allDescendantsOf (" ModalViewContainer" ).let { nodes ->
206+ onAllDescendantsOf (" ModalViewContainer" ).let { modal ->
208207 columnValues.forEach {
209- nodes.findFirstWithText (it.key).assertExists()
208+ modal.find (it.key).assertExists()
210209 if (it.key != " IsFirstOwner" ) { // not able to check checkbox state
211- nodes.findFirstWithText (it.value).assertExists()
210+ modal.find (it.value).assertExists()
212211 }
213212 }
214- nodes.findFirstWithText (" model" ).performTextReplacement(" Fiesta" )
215- nodes.findFirstWithText (" Submit" ).performScrollTo().performClick()
213+ modal.find (" model" ).performTextReplacement(" Fiesta" )
214+ modal.find (" Submit" ).performScrollTo().performClick()
216215 }
217216 waitUntilDoesNotExist(hasText(" Edit Record" ), timeoutMillis = 3000 )
218217 // verify updated record in table
219- waitForNodes (" Fiesta" , count = 2 )
218+ waitForTableNode (" Fiesta" )
220219
221220 // adding new record via popup
222221 onNodeWithText(" + Add cars" ).performScrollTo().performClick()
223222 waitForNode(" Add Record" )
224- allDescendantsOf (" ModalViewContainer" ).let { nodes ->
225- nodes.findFirstWithText (" Submit" ).performScrollTo().performClick()
226- waitUntilAtLeastOneExists(nodes , hasText(" brand: Cannot be blank" ))
223+ onAllDescendantsOf (" ModalViewContainer" ).let { modal ->
224+ modal.find (" Submit" ).performScrollTo().performClick()
225+ waitUntilExactlyOneExists(modal , hasText(" brand: Cannot be blank" ))
227226
228- nodes.findFirstWithText (" brand" ).performTextReplacement(" Opel" )
229- nodes.findFirstWithText (" model" ).performTextReplacement(" Astra" )
230- nodes.findFirstWithText (" Submit" ).performScrollTo().performClick()
227+ modal.find (" brand" ).performTextReplacement(" Opel" )
228+ modal.find (" model" ).performTextReplacement(" Astra" )
229+ modal.find (" Submit" ).performScrollTo().performClick()
231230 }
232- waitUntilDoesNotExist(hasText(" Add Record" ))
231+ waitUntilDoesNotExist(hasText(" Add Record" ), timeoutMillis = 3000 )
233232 // verify new record in table
234- waitForNodes (" Opel" , count = 2 )
235- waitForNodes (" Astra" , count = 2 )
233+ waitForTableNode (" Opel" )
234+ waitForTableNode (" Astra" )
236235
237236 // Step 3 - readonly simple table
238237 onNodeWithText(" Next" ).performClick()
@@ -306,32 +305,32 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
306305 isEditable : Boolean
307306 ) {
308307 with (nodes) {
309- findFirstWithText (" Audi" ).assertExists()
310- findFirstWithText (" A5" ).assertExists()
311- findFirstWithText (" 123000" ).assertExists()
308+ find (" Audi" ).assertExists()
309+ find (" A5" ).assertExists()
310+ find (" 123000" ).assertExists()
312311 if (! isEditable) {
313- findFirstWithText (" Yes" ).assertExists()
312+ find (" Yes" ).assertExists()
314313 }
315- findFirstWithText (" comfort" ).assertExists()
316- findFirstWithText (" gold" ).assertExists()
317- findFirstWithText (expectedDate).assertExists()
318- findFirstWithText (" 12:00 AM" ).assertExists()
319- findFirstWithText (" $expectedDate 12:00 AM" ).assertExists()
320- findFirstWithText (" Notes" ).performScrollTo()
321- waitUntilAtLeastOneExists (nodes, hasText(" This is a note" ), timeoutMillis = 5000L )
314+ find (" comfort" ).assertExists()
315+ find (" gold" ).assertExists()
316+ find (expectedDate).assertExists()
317+ find (" 12:00 AM" ).assertExists()
318+ find (" $expectedDate 12:00 AM" ).assertExists()
319+ find (" Notes" ).performScrollTo()
320+ waitUntilExactlyOneExists (nodes, hasText(" This is a note" ), timeoutMillis = 5000L )
322321 }
323322 }
324323
325324 private fun ComposeUiTest.verifyReadonlyTable (columnValues : MutableMap <String , String >) {
326325 // verify columns
327- columnValues.keys.forEach { waitForNodes (it.uppercase(), count = 2 ) }
326+ columnValues.keys.forEach { waitForTableNode (it.uppercase()) }
328327 // verify table data
329328 columnValues[" model" ] = " Fiesta" // updated model name
330329 columnValues.values.forEach {
331- waitForNodes (it, count = 2 )
330+ waitForTableNode (it)
332331 }
333- waitForNodes (" Opel" , count = 2 )
334- waitForNodes (" Astra" , count = 2 )
332+ waitForTableNode (" Opel" )
333+ waitForTableNode (" Astra" )
335334 // verify absence of add/edit/delete actions
336335 waitUntilDoesNotExist(hasText(" + Add cars" ))
337336 waitUntilDoesNotExist(hasContentDescription(" Edit item 1" ))
@@ -341,22 +340,19 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
341340
342341 private fun ComposeUiTest.performTextInput (testTag : String , inputText : String ) {
343342 waitUntilAtLeastOneExists(hasTestTag(testTag))
344- onAllNodes(hasAnyAncestor(hasTestTag(testTag)))
345- .filter(hasSetTextAction())
346- .onFirst().performTextInput(inputText)
343+ onAllDescendantsOf(testTag).filter(hasSetTextAction()).onFirst().performTextInput(inputText)
347344 }
348345
349346 private fun ComposeUiTest.performClick (testTag : String ) {
350347 waitUntilAtLeastOneExists(hasTestTag(testTag))
351- onAllNodes(hasAnyAncestor(hasTestTag(testTag)))
352- .filter(hasClickAction())
353- .onFirst().performScrollTo().performClick()
348+ onAllDescendantsOf(testTag)
349+ .filter(hasClickAction()).onFirst().performScrollTo().performClick()
354350 }
355351
356- private fun SemanticsNodeInteractionCollection.findFirstWithText (text : String ) =
352+ private fun SemanticsNodeInteractionCollection.find (text : String ) =
357353 this .filter(hasText(text)).onFirst()
358354
359- private fun ComposeUiTest.waitUntilAtLeastOneExists (
355+ private fun ComposeUiTest.waitUntilExactlyOneExists (
360356 nodes : SemanticsNodeInteractionCollection ,
361357 matcher : SemanticsMatcher ,
362358 timeoutMillis : Long = 5000L
@@ -366,9 +362,13 @@ class EmbeddedDataTest : ComposeTest(PegaVersion.v24_2_2) {
366362 }
367363 }
368364
369- private fun ComposeUiTest. allDescendantsOf (testTag : String ) =
365+ private fun SemanticsNodeInteractionsProvider. onAllDescendantsOf (testTag : String ) =
370366 onAllNodes(hasAnyAncestor(hasTestTag(testTag)))
371367
372368 private fun SemanticsNodeInteractionCollection.filterDescendantsOf (testTag : String ) =
373369 filter(hasAnyAncestor(hasTestTag(testTag)))
370+
371+ // Only one table node with the given text is visible,
372+ // but for some reason the test framework detects two.
373+ private fun ComposeUiTest.waitForTableNode (text : String ) = waitForNodes(text, count = 2 )
374374}
0 commit comments