Thread
#compose
    a

    Arun

    1 year ago
    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:
    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.
    composeTestRule.onNodeWithTag("Section Header").assertIsDisplayed()
    Any idea on what am I doing wrong?
    Apologies for such a long post here.
    jim

    jim

    1 year ago
    cc @Filip Pavlis
    f

    Filip Pavlis

    1 year ago
    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:
    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.
    a

    Arun

    1 year ago
    Got it. Thanks for the great explanation.