Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement - Add support for content checking and tint color for compose icon #70

Merged
merged 1 commit into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package io.github.kakaocup.compose.node.assertion

import androidx.annotation.DrawableRes
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert

interface ImageContentAssertions : NodeAssertions {
val imageContentSemanticsPropertyKey: SemanticsPropertyKey<Any>

/**
* Asserts that the image or icon content contains the given [imageVector].
*
* Throws [AssertionError] if the image or icon content value is not equal to `imageVector`.
* Throws [IllegalStateException] if the image or icon does not contain the [imageContentSemanticsPropertyKey] modifier.
*/
fun assertContentEquals(imageVector: ImageVector) {
delegate.check(NodeAssertions.ComposeBaseAssertionType.ASSERT_VALUE_EQUALS) { assert(hasImageContent(imageVector)) }
}

/**
* Asserts that the image or icon content contains the given [drawableRes].
*
* Throws [AssertionError] if the image or icon content value is not equal to `drawableRes`.
* Throws [IllegalStateException] if the image or icon does not contain the [imageContentSemanticsPropertyKey] modifier.
*/
fun assertContentEquals(@DrawableRes drawableRes: Int) {
delegate.check(NodeAssertions.ComposeBaseAssertionType.ASSERT_VALUE_EQUALS) { assert(hasImageContent(drawableRes)) }
}

private fun hasImageContent(expectedContent: Any): SemanticsMatcher = SemanticsMatcher(
"The content is expected to be $expectedContent, but the actual content is different"
) { node ->
val actual = node.config.getOrNull(imageContentSemanticsPropertyKey)
?: error("Compose view does not contain $imageContentSemanticsPropertyKey modifier")

return@SemanticsMatcher actual == expectedContent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.github.kakaocup.compose.node.assertion

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
import io.github.kakaocup.compose.utilities.getComposeColor

interface TintColorAssertions : NodeAssertions {
val tintColorSemanticsPropertyKey: SemanticsPropertyKey<Color>

/**
* Asserts that the compose view tint color contains the given [color].
*
* Throws [AssertionError] if the compose view tint color value is not equal to `color`.
* Throws [IllegalStateException] if the compose view does not contain the [tintColorSemanticsPropertyKey] modifier.
*/
fun assertTintColorEquals(color: Color) {
delegate.check(NodeAssertions.ComposeBaseAssertionType.ASSERT_VALUE_EQUALS) { assert(hasColor(color)) }
}

/**
* Asserts that the compose view tint color contains the given [color].
*
* Throws [AssertionError] if the compose view tint color value is not equal to `color`.
* Throws [IllegalStateException] if the compose view does not contain the [tintColorSemanticsPropertyKey] modifier.
* Throws [IllegalArgumentException] if the color value is incorrect.
*/
fun assertTintColorEquals(color: String) {
delegate.check(NodeAssertions.ComposeBaseAssertionType.ASSERT_VALUE_EQUALS) { assert(hasColor(color)) }
}

/**
* Asserts that the compose view tint color contains the given [color].
*
* Throws [AssertionError] if the compose view tint color value is not equal to `color`.
* Throws [IllegalStateException] if the compose view does not contain the [tintColorSemanticsPropertyKey] modifier.
*/
fun assertTintColorEquals(color: Long) {
delegate.check(NodeAssertions.ComposeBaseAssertionType.ASSERT_VALUE_EQUALS) { assert(hasColor(color)) }
}

private fun hasColor(expectedColor: Color): SemanticsMatcher = SemanticsMatcher(
"The color is expected to be $expectedColor, but the actual color is different"
) { node ->
val actualColor = node.config.getOrNull(tintColorSemanticsPropertyKey)
?: error("Compose view does not contain $tintColorSemanticsPropertyKey modifier")

return@SemanticsMatcher actualColor == expectedColor
}

private fun hasColor(expectedColor: String): SemanticsMatcher = SemanticsMatcher(
"The color is expected to be $expectedColor, but the actual color is different"
) { node ->
val actualColor = node.config.getOrNull(tintColorSemanticsPropertyKey)
?: error("Compose view does not contain $tintColorSemanticsPropertyKey modifier")

return@SemanticsMatcher actualColor == getComposeColor(expectedColor)
}

private fun hasColor(expectedColor: Long): SemanticsMatcher = SemanticsMatcher(
"The color is expected to be $expectedColor, but the actual color is different"
) { node ->
val actualColor = node.config.getOrNull(tintColorSemanticsPropertyKey)
?: error("Compose view does not contain $tintColorSemanticsPropertyKey modifier")

return@SemanticsMatcher actualColor == Color(expectedColor)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.github.kakaocup.compose.node.element

import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import io.github.kakaocup.compose.node.assertion.ImageContentAssertions
import io.github.kakaocup.compose.node.assertion.TintColorAssertions
import io.github.kakaocup.compose.node.builder.NodeMatcher
import io.github.kakaocup.compose.node.core.BaseNode

abstract class KIconNode(
semanticsProvider: SemanticsNodeInteractionsProvider,
nodeMatcher: NodeMatcher,
parentNode: BaseNode<*>?,
useUnmergedTree: Boolean = false
) : BaseNode<KIconNode>(
semanticsProvider = semanticsProvider,
nodeMatcher = nodeMatcher.copy(useUnmergedTree = useUnmergedTree),
parentNode = parentNode
), ImageContentAssertions, TintColorAssertions
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.github.kakaocup.compose.utilities

import androidx.compose.ui.graphics.Color

internal fun getComposeColor(color: String): Color {
val normalizeColor = if (color.contains("#")) color else "#$color"
return Color(android.graphics.Color.parseColor(normalizeColor))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.github.kakaocup.compose.node

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import io.github.kakaocup.compose.node.builder.NodeMatcher
import io.github.kakaocup.compose.node.core.BaseNode
import io.github.kakaocup.compose.node.element.KIconNode
import io.github.kakaocup.compose.sample.semantics.ImageContentSemanticKey
import io.github.kakaocup.compose.sample.semantics.TintColorSemanticKey

class KAppIconNode(
semanticsProvider: SemanticsNodeInteractionsProvider,
nodeMatcher: NodeMatcher,
parentNode: BaseNode<*>? = null,
) : KIconNode(
semanticsProvider = semanticsProvider,
nodeMatcher = nodeMatcher,
parentNode = parentNode,
useUnmergedTree = true
) {
override val imageContentSemanticsPropertyKey: SemanticsPropertyKey<Any> = ImageContentSemanticKey
override val tintColorSemanticsPropertyKey: SemanticsPropertyKey<Color> = TintColorSemanticKey
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.kakaocup.compose.screen

import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import io.github.kakaocup.compose.node.KAppIconNode
import io.github.kakaocup.compose.node.element.ComposeScreen
import io.github.kakaocup.compose.node.element.KNode

Expand All @@ -20,6 +21,18 @@ class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
hasPosition(1)
}

val changeIconsButton: KNode = child {
hasTestTag("changeIconButton")
}

val iconDrawableRes: KAppIconNode = child {
hasTestTag("iconDrawableRes")
}

val iconImageVector: KAppIconNode = child {
hasTestTag("iconImageVector")
}

val myButton: KNode = child {
hasTestTag("myTestButton")
hasText("Button 1")
Expand All @@ -29,4 +42,4 @@ class MainActivityScreen(semanticsProvider: SemanticsNodeInteractionsProvider) :
val clickCounter: KNode = child {
hasTestTag("clickCounter")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package io.github.kakaocup.compose.test

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Call
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import io.github.kakaocup.compose.sample.MainActivity
import io.github.kakaocup.compose.node.element.ComposeScreen.Companion.onComposeScreen
import io.github.kakaocup.compose.sample.MainActivity
import io.github.kakaocup.compose.sample.R
import io.github.kakaocup.compose.screen.MainActivityScreen
import org.junit.Rule
import org.junit.Test
Expand All @@ -18,7 +23,7 @@ class SimpleTest {
myButton {
assertIsDisplayed()
assertTextContains("Button 1")
assertTextContains(io.github.kakaocup.compose.sample.R.string.button_1)
assertTextContains(R.string.button_1)
}

myText1 {
Expand All @@ -38,4 +43,50 @@ class SimpleTest {
}
}
}
}

@Test
fun iconTest() {
onComposeScreen<MainActivityScreen>(composeTestRule) {
changeIconsButton {
assertIsDisplayed()
}
iconDrawableRes {
assertIsDisplayed()
assertContentEquals(R.drawable.ic_android)
assertTintColorEquals(Color.Black)
assertTintColorEquals("000000")
assertTintColorEquals("#000000")
assertTintColorEquals(0xFF000000)
}

iconImageVector {
assertIsDisplayed()
assertContentEquals(Icons.Filled.AccountCircle)
assertTintColorEquals(Color.Black)
assertTintColorEquals("000000")
assertTintColorEquals("#000000")
assertTintColorEquals(0xFF000000)
}

changeIconsButton.performClick()

iconDrawableRes {
assertIsDisplayed()
assertContentEquals(R.drawable.ic_adb)
assertTintColorEquals(Color.Blue)
assertTintColorEquals("0000FF")
assertTintColorEquals("#0000FF")
assertTintColorEquals(0xFF0000FF)
}

iconImageVector {
assertIsDisplayed()
assertContentEquals(Icons.Filled.Call)
assertTintColorEquals(Color.Blue)
assertTintColorEquals("0000FF")
assertTintColorEquals("#0000FF")
assertTintColorEquals(0xFF0000FF)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package io.github.kakaocup.compose.sample

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Call
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import io.github.kakaocup.compose.sample.semantics.imageContentSemantic
import io.github.kakaocup.compose.sample.semantics.tintColorSemantic

@Composable
fun MainScreen() {
var tintColor by remember { mutableStateOf(Color.Black) }
var iconRes by remember { mutableIntStateOf(R.drawable.ic_android) }
var iconImageVector by remember { mutableStateOf(Icons.Filled.AccountCircle) }

Column(
modifier = Modifier
.fillMaxSize()
Expand All @@ -39,6 +54,41 @@ fun MainScreen() {
.semantics { testTag = "mySimpleText" }
)

Row {
Icon(
modifier = Modifier
.imageContentSemantic(iconRes)
.tintColorSemantic(tintColor)
.testTag("iconDrawableRes"),
painter = painterResource(id = iconRes),
tint = tintColor,
contentDescription = null
)
Icon(
modifier = Modifier
.imageContentSemantic(iconImageVector)
.tintColorSemantic(tintColor)
.testTag("iconImageVector"),
imageVector = iconImageVector,
tint = tintColor,
contentDescription = null
)
}

Button(
content = {
Text(text = stringResource(R.string.button_change_icon))
},
onClick = {
tintColor = Color.Blue
iconRes = R.drawable.ic_adb
iconImageVector = Icons.Filled.Call
},
modifier = Modifier
.padding(8.dp)
.semantics { testTag = "changeIconButton" }
)

Button(
content = {
Text(text = stringResource(R.string.button_1))
Expand Down Expand Up @@ -72,4 +122,4 @@ private fun MainScreenPreview() {
MaterialTheme {
MainScreen()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.kakaocup.compose.sample.semantics

import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.semantics

val TintColorSemanticKey = SemanticsPropertyKey<Color>("TintColor")
var SemanticsPropertyReceiver.tintColor by TintColorSemanticKey
fun Modifier.tintColorSemantic(color: Color): Modifier {
return semantics { tintColor = color }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.github.kakaocup.compose.sample.semantics

import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.SemanticsPropertyKey
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.semantics

val ImageContentSemanticKey = SemanticsPropertyKey<Any>("ImageContent")
var SemanticsPropertyReceiver.imageContent by ImageContentSemanticKey
fun Modifier.imageContentSemantic(imageContent: Any): Modifier {
return semantics { this.imageContent = imageContent }
}
Loading