feat: add centralized TestTags and testTag modifiers (Closes #31) #44

Merged
shahondin1624 merged 1 commits from feature/issue-31-test-tags-on-all-key-components into main 2026-03-13 13:04:01 +01:00
5 changed files with 129 additions and 16 deletions

View File

@@ -17,9 +17,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.compose.ui.tooling.preview.Preview
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants import org.shahondin1624.lib.components.UiConstants
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
import org.shahondin1624.model.EXAMPLE_CHARACTER import org.shahondin1624.model.EXAMPLE_CHARACTER
@@ -73,7 +75,9 @@ private fun AppContent(
onClick = { onClick = {
scope.launch { drawerState.close() } scope.launch { drawerState.close() }
}, },
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp) modifier = Modifier
.padding(horizontal = 12.dp, vertical = 4.dp)
.testTag(TestTags.NAV_CHARACTER_SHEET)
) )
NavigationDrawerItem( NavigationDrawerItem(
@@ -84,7 +88,9 @@ private fun AppContent(
scope.launch { drawerState.close() } scope.launch { drawerState.close() }
// TODO: Navigate to settings // TODO: Navigate to settings
}, },
modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp) modifier = Modifier
.padding(horizontal = 12.dp, vertical = 4.dp)
.testTag(TestTags.NAV_SETTINGS)
) )
} }
} }
@@ -95,20 +101,26 @@ private fun AppContent(
TopAppBar( TopAppBar(
title = { Text("Shadowrun Character Sheet") }, title = { Text("Shadowrun Character Sheet") },
navigationIcon = { navigationIcon = {
IconButton(onClick = { IconButton(
scope.launch { onClick = {
if (drawerState.isClosed) { scope.launch {
drawerState.open() if (drawerState.isClosed) {
} else { drawerState.open()
drawerState.close() } else {
drawerState.close()
}
} }
} },
}) { modifier = Modifier.testTag(TestTags.MENU_BUTTON)
) {
Icon(Icons.Default.Menu, contentDescription = "Menu") Icon(Icons.Default.Menu, contentDescription = "Menu")
} }
}, },
actions = { actions = {
IconButton(onClick = { isDark = !isDark }) { IconButton(
onClick = { isDark = !isDark },
modifier = Modifier.testTag(TestTags.THEME_TOGGLE)
) {
Icon( Icon(
imageVector = if (isDark) { imageVector = if (isDark) {
Icons.Default.LightMode Icons.Default.LightMode

View File

@@ -0,0 +1,35 @@
package org.shahondin1624.lib.components
/**
* Centralized test tag strings for Compose UI test identification.
*
* Usage: `Modifier.testTag(TestTags.attributeCard("Body"))`
*
* Tags that reference components not yet implemented are included so that
* future stories can import them without creating a new file.
*/
object TestTags {
// --- Attribute cards ---
fun attributeCard(type: String): String = "attribute_card_${type.lowercase()}"
// --- Talent cards ---
fun talentCard(name: String): String = "talent_card_${name.lowercase().replace(" ", "_")}"
// --- Panels (defined for future character display stories) ---
const val PANEL_CHARACTER_HEADER = "panel_character_header"
const val PANEL_RESOURCES = "panel_resources"
const val PANEL_DERIVED_ATTRIBUTES = "panel_derived_attributes"
const val PANEL_DAMAGE_MONITOR = "panel_damage_monitor"
// --- Navigation items ---
const val NAV_CHARACTER_SHEET = "nav_character_sheet"
const val NAV_SETTINGS = "nav_settings"
// --- Dice / roll buttons ---
fun rollButton(name: String): String = "roll_button_${name.lowercase().replace(" ", "_")}"
// --- Top app bar ---
const val TOP_APP_BAR = "top_app_bar"
const val THEME_TOGGLE = "theme_toggle"
const val MENU_BUTTON = "menu_button"
}

View File

@@ -14,9 +14,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING
import org.shahondin1624.lib.functions.DiceRoll import org.shahondin1624.lib.functions.DiceRoll
import org.shahondin1624.model.attributes.Attribute import org.shahondin1624.model.attributes.Attribute
@@ -33,7 +35,7 @@ fun Attribute(
) { ) {
var isInDarkMode by LocalThemeIsDark.current var isInDarkMode by LocalThemeIsDark.current
val textColor = if (isInDarkMode) Color.Black else Color.White val textColor = if (isInDarkMode) Color.Black else Color.White
Card { Card(modifier = Modifier.testTag(TestTags.attributeCard(attribute.type.name))) {
Row( Row(
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically, verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start horizontalArrangement = Arrangement.Start
@@ -64,7 +66,9 @@ fun Attribute(
val result = attribute.test() val result = attribute.test()
onRoll(result) onRoll(result)
}, },
modifier = Modifier.padding(end = SMALL_PADDING) modifier = Modifier
.padding(end = SMALL_PADDING)
.testTag(TestTags.rollButton(attribute.type.name))
) { ) {
Image( Image(
painter = painterResource(Res.drawable.dice), painter = painterResource(Res.drawable.dice),

View File

@@ -20,9 +20,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING
import org.shahondin1624.lib.functions.DiceRoll import org.shahondin1624.lib.functions.DiceRoll
import org.shahondin1624.model.attributes.Attributes import org.shahondin1624.model.attributes.Attributes
@@ -41,7 +43,7 @@ fun Talent(
) { ) {
var isInDarkMode by LocalThemeIsDark.current var isInDarkMode by LocalThemeIsDark.current
val textColor = if (isInDarkMode) Color.Black else Color.White val textColor = if (isInDarkMode) Color.Black else Color.White
Card { Card(modifier = Modifier.testTag(TestTags.talentCard(talent.name))) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start horizontalArrangement = Arrangement.Start
@@ -84,7 +86,9 @@ fun Talent(
val result = talent.test(modifiers = emptyList(), attributes = attributes) val result = talent.test(modifiers = emptyList(), attributes = attributes)
onRoll(result) onRoll(result)
}, },
modifier = Modifier.padding(end = SMALL_PADDING) modifier = Modifier
.padding(end = SMALL_PADDING)
.testTag(TestTags.rollButton(talent.name))
) { ) {
Image( Image(
painter = painterResource(Res.drawable.dice), painter = painterResource(Res.drawable.dice),

View File

@@ -0,0 +1,58 @@
package org.shahondin1624
import org.shahondin1624.lib.components.TestTags
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class TestTagsTest {
@Test
fun attributeCardTagFormat() {
assertEquals("attribute_card_body", TestTags.attributeCard("Body"))
assertEquals("attribute_card_agility", TestTags.attributeCard("Agility"))
assertEquals("attribute_card_charisma", TestTags.attributeCard("Charisma"))
}
@Test
fun talentCardTagFormat() {
assertEquals("talent_card_firearms", TestTags.talentCard("Firearms"))
assertEquals("talent_card_close_combat", TestTags.talentCard("Close Combat"))
}
@Test
fun rollButtonTagFormat() {
assertEquals("roll_button_body", TestTags.rollButton("Body"))
assertEquals("roll_button_close_combat", TestTags.rollButton("Close Combat"))
}
@Test
fun navTagsAreDefined() {
assertEquals("nav_character_sheet", TestTags.NAV_CHARACTER_SHEET)
assertEquals("nav_settings", TestTags.NAV_SETTINGS)
}
@Test
fun panelTagsAreDefined() {
assertTrue(TestTags.PANEL_CHARACTER_HEADER.isNotBlank())
assertTrue(TestTags.PANEL_RESOURCES.isNotBlank())
assertTrue(TestTags.PANEL_DERIVED_ATTRIBUTES.isNotBlank())
assertTrue(TestTags.PANEL_DAMAGE_MONITOR.isNotBlank())
}
@Test
fun allTagsAreUnique() {
val staticTags = listOf(
TestTags.PANEL_CHARACTER_HEADER,
TestTags.PANEL_RESOURCES,
TestTags.PANEL_DERIVED_ATTRIBUTES,
TestTags.PANEL_DAMAGE_MONITOR,
TestTags.NAV_CHARACTER_SHEET,
TestTags.NAV_SETTINGS,
TestTags.TOP_APP_BAR,
TestTags.THEME_TOGGLE,
TestTags.MENU_BUTTON
)
assertEquals(staticTags.size, staticTags.toSet().size, "All static tags should be unique")
}
}