https://kotlinlang.org logo
a

Arun

11/20/2020, 12:23 PM
Copy code
Node #1 at (0.0, 66.0, 1080.0, 2009.0)px
     |-Node #2 at (970.0, 110.0, 1036.0, 176.0)px
     | OnClick = 'AccessibilityAction(label=null, action=Function0<java.lang.Boolean>)'
     | MergeDescendants = 'true'
     |-Node #3 at (0.0, 220.0, 1080.0, 2009.0)px
       VerticalAccessibilityScrollState = 'AccessibilityScrollState(value=0.0, maxValue=0.0, reverseScrolling=false)'
       ScrollBy = 'AccessibilityAction(label=null, action=Function2<java.lang.Float, java.lang.Float, java.lang.Boolean>)'
        |-Node #4 at (44.0, 1089.0, 368.0, 1152.0)px, Tag: 'Section Header'
        | Text = 'Section Header'
        | GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
        |-Node #7 at (44.0, 1196.0, 1036.0, 1423.0)px
        | OnClick = 'AccessibilityAction(label=null, action=Function0<java.lang.Boolean>)'
        | MergeDescendants = 'true'
        |  |-Node #8 at (88.0, 1229.0, 201.0, 1283.0)px, Tag: 'Title 1'
        |  | Text = 'Title 1'
        |  | GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
        |  |-Node #11 at (88.0, 1289.0, 794.0, 1379.0)px
        |    Text = 'Get the list of all the assigned tasks in serial order to complete'
        |    GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
        |-Node #13 at (44.0, 1467.0, 1036.0, 1694.0)px
        | OnClick = 'AccessibilityAction(label=null, action=Function0<java.lang.Boolean>)'
        | MergeDescendants = 'true'
        |  |-Node #14 at (88.0, 1500.0, 201.0, 1554.0)px, Tag: 'Title 2'
        |  | Text = 'Title 2'
        |  | GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
        |  |-Node #17 at (88.0, 1560.0, 794.0, 1650.0)px
        |    Text = 'Perform all IoT actions and QC of all devices in the car'
        |    GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
        |-Node #19 at (44.0, 1738.0, 1036.0, 1965.0)px
          OnClick = 'AccessibilityAction(label=null, action=Function0<java.lang.Boolean>)'
          MergeDescendants = 'true'
           |-Node #20 at (88.0, 1771.0, 201.0, 1825.0)px, Tag: 'Title 3'
           | Text = 'Title 3'
           | GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
           |-Node #23 at (88.0, 1831.0, 794.0, 1921.0)px
             Text = 'Get the list of all the assigned tasks in serial order to complete'
             GetTextLayoutResult = 'AccessibilityAction(label=null, action=(kotlin.collections.MutableList<androidx.compose.ui.text.TextLayoutResult>) -> kotlin.Boolean)'
The following test fails even though, you can clearly see in the tree that there are Nodes with Tags
Section Header
and `Title 1`:
Copy code
fun apiSuccess_showFeatures(): Unit = testCoroutineDispatcher.runBlockingTest {
    composeTestRule.onRoot(useUnmergedTree = true).printToLog("TAG")

    composeTestRule.onNodeWithTag("Section Header").assertIsDisplayed()
    composeTestRule.onNodeWithTag("Title 1").assertIsDisplayed()
}
If I just test with the following single assertion, the test passes.
Copy code
composeTestRule.onNodeWithTag("Section Header").assertIsDisplayed()
Any idea on what am I doing wrong?
Apologies for such a long post here.
j

jim

11/20/2020, 12:27 PM
cc @Filip Pavlis
f

Filip Pavlis

11/20/2020, 2:12 PM
The reason here is that the node with "Title 1" tag gets merged into the parent (you can see that the parent defines MergeDescendants). When we merge a child into its parent, we throw away child's test tag. We don't want parents to be annotated by child test tags as it might confuse you on what elements you are dealing with. The reason the printing worked for you is because you passed
useUnmergedTree = true
. If you would pass this to the
onNodeWithTag
as well it would actually find you the element. Here is the piece of code that defines the merging policy:
Copy code
val TestTag = SemanticsPropertyKey<String>(
        name = "TestTag",
        mergePolicy = { parentValue, _ ->
            // Never merge TestTags, to avoid leaking internal test tags to parents.
            parentValue
        }
    )
This is actually also good feedback for us. We might be able to check that the tag exists in the unmerged tree and throw an error that would explain this properly.
👍 2
a

Arun

11/20/2020, 7:40 PM
Got it. Thanks for the great explanation.
4 Views