feat: add centralized TestTags and testTag modifiers on all key UI components (Closes #31)
Create TestTags.kt with tag generators for attribute cards, talent cards, roll buttons, nav items, and panel placeholders. Apply testTag modifiers to Attribute, Talent, and App navigation/toolbar composables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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,9 +75,11 @@ 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(
|
||||||
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
|
||||||
label = { Text("Settings") },
|
label = { Text("Settings") },
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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),
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user