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

This commit was merged in pull request #44.
This commit is contained in:
2026-03-13 13:04:01 +01:00
parent 8855f48ae2
commit 6e5a6a3fa1
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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
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.charactermodel.attributespage.AttributesPage
import org.shahondin1624.model.EXAMPLE_CHARACTER
@@ -73,9 +75,11 @@ private fun AppContent(
onClick = {
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(
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
label = { Text("Settings") },
@@ -84,7 +88,9 @@ private fun AppContent(
scope.launch { drawerState.close() }
// 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(
title = { Text("Shadowrun Character Sheet") },
navigationIcon = {
IconButton(onClick = {
scope.launch {
if (drawerState.isClosed) {
drawerState.open()
} else {
drawerState.close()
IconButton(
onClick = {
scope.launch {
if (drawerState.isClosed) {
drawerState.open()
} else {
drawerState.close()
}
}
}
}) {
},
modifier = Modifier.testTag(TestTags.MENU_BUTTON)
) {
Icon(Icons.Default.Menu, contentDescription = "Menu")
}
},
actions = {
IconButton(onClick = { isDark = !isDark }) {
IconButton(
onClick = { isDark = !isDark },
modifier = Modifier.testTag(TestTags.THEME_TOGGLE)
) {
Icon(
imageVector = if (isDark) {
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.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.painterResource
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING
import org.shahondin1624.lib.functions.DiceRoll
import org.shahondin1624.model.attributes.Attribute
@@ -33,7 +35,7 @@ fun Attribute(
) {
var isInDarkMode by LocalThemeIsDark.current
val textColor = if (isInDarkMode) Color.Black else Color.White
Card {
Card(modifier = Modifier.testTag(TestTags.attributeCard(attribute.type.name))) {
Row(
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
@@ -64,7 +66,9 @@ fun Attribute(
val result = attribute.test()
onRoll(result)
},
modifier = Modifier.padding(end = SMALL_PADDING)
modifier = Modifier
.padding(end = SMALL_PADDING)
.testTag(TestTags.rollButton(attribute.type.name))
) {
Image(
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.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.painterResource
import org.shahondin1624.lib.components.TestTags
import org.shahondin1624.lib.components.UiConstants.SMALL_PADDING
import org.shahondin1624.lib.functions.DiceRoll
import org.shahondin1624.model.attributes.Attributes
@@ -41,7 +43,7 @@ fun Talent(
) {
var isInDarkMode by LocalThemeIsDark.current
val textColor = if (isInDarkMode) Color.Black else Color.White
Card {
Card(modifier = Modifier.testTag(TestTags.talentCard(talent.name))) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
@@ -84,7 +86,9 @@ fun Talent(
val result = talent.test(modifiers = emptyList(), attributes = attributes)
onRoll(result)
},
modifier = Modifier.padding(end = SMALL_PADDING)
modifier = Modifier
.padding(end = SMALL_PADDING)
.testTag(TestTags.rollButton(talent.name))
) {
Image(
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")
}
}