16

I'm trying to test a Text that on my component I can print it in different colors, so on my test I'm verifying it gets the expected color. I was looking for a method to return the color but I did not find any.

From now I'm asserting that the text is correct and the visibility is correct, but when trying to find the method to get the colour I get too deep and I'm looking for a simpler solution.

composeTestRule.onNode(hasTestTag("testTagForButton"), true)
            .assertExists()
            .assertTextEquals("Testing")

I've check that I can do something like .fetchSemanticsNode().layoutInfo.getModifierInfo() to get into the Modifier and perhaps from there I can get the colour, but it's too much maybe. Also I've found this .captureToImage() that perhaps I could get the colour on it, but since I had to put the pixels I decided that it's not the way.

Is there any simple way to get that?

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148

2 Answers2

14

I am by no means a compose expert, but just looking at compose source code, you could utilize their GetTextLayoutResult accessibility semantic action. This will contain all the properties that are used to render the Text on a canvas.

Some quick and dirty extension functions I put up for convenience:

fun SemanticsNodeInteraction.assertTextColor(
    color: Color
): SemanticsNodeInteraction = assert(isOfColor(color))

private fun isOfColor(color: Color): SemanticsMatcher = SemanticsMatcher(
    "${SemanticsProperties.Text.name} is of color '$color'"
) {
    val textLayoutResults = mutableListOf<TextLayoutResult>()
    it.config.getOrNull(SemanticsActions.GetTextLayoutResult)
        ?.action
        ?.invoke(textLayoutResults)
    return@SemanticsMatcher if (textLayoutResults.isEmpty()) {
        false
    } else {
        textLayoutResults.first().layoutInput.style.color == color
    }
}

Which can be then used like this:

composeTestRule.onNode(hasTestTag("testTagForButton"), true)
            .assertExists()
            .assertTextEquals("Testing")
            .assertTextColor(Color.Black)
romtsn
  • 11,704
  • 2
  • 31
  • 49
  • Thanks for answering, I'm going to try it and come back to you with feedback but have you found to check the background color of a `Row` / `Column`? – Skizo-ozᴉʞS ツ Feb 11 '22 at 10:50
  • Nope, but your initial hunch was correct - you'd need to look through the `getModifierInfo()` list, find the background color modifier and compare it with your expected color. I don't think there's any other way to do it, at least I haven't found one. Since you set the background color for rows and columns through a modifier, that's what you need to look for. – romtsn Feb 11 '22 at 12:21
  • Yes, but there's no "getColor" from Modifiers nor getBackgroundColor – Skizo-ozᴉʞS ツ Feb 14 '22 at 13:43
  • Oh shit, I didn't really expect compose to be so restrictive (well, it's good, in a way). Looks like the only other way than capturing an image, would be to enable [debug inspector](https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/package-summary#isDebugInspectorInfoEnabled()) and then something like `(getModifierInfo()[1].modifier as InspectableValue).inspectableElements.find { it.name == "color" }?.value as Color`. Note though, that you need to know the exact position of the background modifier in the list, because there's no way to filter out, since it's private – romtsn Feb 14 '22 at 22:50
  • 1
    (and yes, I don't like how it looks, dunno if it's just missing functionality from compose, or it's not supposed to be tested like that at all) – romtsn Feb 14 '22 at 22:53
  • 1
    Actually, I did a bit more research, looks like the designed way to do this is to add a custom `semantics` modifier with the properties you're interested in to use in your tests. [Link](https://developer.android.com/jetpack/compose/testing#custom-semantics-properties). This requires changing your prod code though, which is not great. – romtsn Feb 15 '22 at 07:51
  • Yes, I've found it also, I did not like tbh... – Skizo-ozᴉʞS ツ Feb 15 '22 at 07:52
  • The [link](https://developer.android.com/jetpack/compose/testing#custom-semantics-properties) you found in your research also states that "...Using custom Semantics properties to expose visual properties such as colors, font size or rounded corner radius is not recommended, as it can pollute production code and wrong". – Ugo Jul 21 '23 at 08:44
3

I am unable to comment post above, also didn't find question about checking background color, so decide to place my version here.

private fun hasBackground(node: SemanticsNode, color: Color, shape: Shape): Boolean {
    return node.layoutInfo.getModifierInfo().filter { modifierInfo ->
        modifierInfo.modifier == Modifier.background(color, shape)
    }.size == 1
}

To test background color and don't touch debug inspection info (this isn't for testing) we are unable to test only background color, but can test whole background by comparing production background (which placed into modifier) with our testing one.

Hope it help somebody.