From c8be6a695ae95c711b01505cee2329bec980d1d9 Mon Sep 17 00:00:00 2001 From: shahondin1624 Date: Fri, 13 Mar 2026 13:43:15 +0100 Subject: [PATCH] test: add dice roll result dialog tests and test tags (Closes #35) Add DiceRollResultDialogTest with 4 Compose UI tests verifying dialog shows dice count, success count, individual die chips, roll label, and can be dismissed via OK button. Add test tags to DiceRollResultDialog and update TestTagsTest to cover new dice roll dialog tags. Co-Authored-By: Claude Opus 4.6 --- .../shahondin1624/lib/components/TestTags.kt | 7 ++ .../charactermodel/DiceRollResultDialog.kt | 19 ++- .../shahondin1624/DiceRollResultDialogTest.kt | 116 ++++++++++++++++++ .../kotlin/org/shahondin1624/TestTagsTest.kt | 16 ++- 4 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 sharedUI/src/commonTest/kotlin/org/shahondin1624/DiceRollResultDialogTest.kt diff --git a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt index 6f0e6f4..9b3162b 100644 --- a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt +++ b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/TestTags.kt @@ -30,6 +30,13 @@ object TestTags { // --- Dice / roll buttons --- fun rollButton(name: String): String = "roll_button_${name.lowercase().replace(" ", "_")}" + // --- Dice roll result dialog --- + const val DICE_ROLL_DIALOG = "dice_roll_dialog" + const val DICE_ROLL_DICE_COUNT = "dice_roll_dice_count" + const val DICE_ROLL_SUCCESS_COUNT = "dice_roll_success_count" + const val DICE_ROLL_DISMISS_BUTTON = "dice_roll_dismiss_button" + fun dieChip(index: Int): String = "die_chip_$index" + // --- Top app bar --- const val TOP_APP_BAR = "top_app_bar" const val THEME_TOGGLE = "theme_toggle" diff --git a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/charactermodel/DiceRollResultDialog.kt b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/charactermodel/DiceRollResultDialog.kt index eafbe88..6084c3b 100644 --- a/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/charactermodel/DiceRollResultDialog.kt +++ b/sharedUI/src/commonMain/kotlin/org/shahondin1624/lib/components/charactermodel/DiceRollResultDialog.kt @@ -8,9 +8,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import org.shahondin1624.lib.components.TestTags import org.shahondin1624.lib.functions.DiceRoll /** @@ -29,6 +31,7 @@ fun DiceRollResultDialog( AlertDialog( onDismissRequest = onDismiss, + modifier = Modifier.testTag(TestTags.DICE_ROLL_DIALOG), title = { Text( text = rollLabel, @@ -46,10 +49,12 @@ fun DiceRollResultDialog( ) { Text( text = "${diceRoll.numberOfDice} dice rolled", + modifier = Modifier.testTag(TestTags.DICE_ROLL_DICE_COUNT), style = MaterialTheme.typography.bodyMedium ) Text( text = "${diceRoll.numberOfSuccesses} successes", + modifier = Modifier.testTag(TestTags.DICE_ROLL_SUCCESS_COUNT), style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = if (diceRoll.numberOfSuccesses > 0) @@ -92,7 +97,10 @@ fun DiceRollResultDialog( } }, confirmButton = { - TextButton(onClick = onDismiss) { + TextButton( + onClick = onDismiss, + modifier = Modifier.testTag(TestTags.DICE_ROLL_DISMISS_BUTTON) + ) { Text("OK") } } @@ -106,14 +114,14 @@ private fun DiceResultGrid(results: List) { horizontalArrangement = Arrangement.spacedBy(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp) ) { - for (value in results) { - DieChip(value) + for ((index, value) in results.withIndex()) { + DieChip(value, index) } } } @Composable -private fun DieChip(value: Int) { +private fun DieChip(value: Int, index: Int) { val isSuccess = value >= 5 val isOne = value == 1 @@ -131,7 +139,8 @@ private fun DieChip(value: Int) { Box( modifier = Modifier .size(32.dp) - .background(backgroundColor, CircleShape), + .background(backgroundColor, CircleShape) + .testTag(TestTags.dieChip(index)), contentAlignment = Alignment.Center ) { Text( diff --git a/sharedUI/src/commonTest/kotlin/org/shahondin1624/DiceRollResultDialogTest.kt b/sharedUI/src/commonTest/kotlin/org/shahondin1624/DiceRollResultDialogTest.kt new file mode 100644 index 0000000..3afe336 --- /dev/null +++ b/sharedUI/src/commonTest/kotlin/org/shahondin1624/DiceRollResultDialogTest.kt @@ -0,0 +1,116 @@ +package org.shahondin1624 + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertTextContains +import androidx.compose.ui.test.onAllNodesWithTag +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.runComposeUiTest +import org.shahondin1624.lib.components.TestTags +import org.shahondin1624.lib.components.charactermodel.DiceRollResultDialog +import org.shahondin1624.lib.functions.DiceRoll +import kotlin.test.Test +import kotlin.test.assertTrue + +@OptIn(ExperimentalTestApi::class) +class DiceRollResultDialogTest { + + /** A deterministic DiceRoll with known results for testing. */ + private fun testDiceRoll() = DiceRoll( + numberOfDice = 6, + numberOfSides = 6, + numberForSuccessOrHigher = 5, + result = listOf(1, 2, 4, 5, 5, 6), + numberOfSuccesses = 3 + ) + + @Test + fun dialogShowsDiceCountAndSuccessCount() = runComposeUiTest { + setContent { + DiceRollResultDialog( + diceRoll = testDiceRoll(), + rollLabel = "Body", + onDismiss = {} + ) + } + + // Verify dialog is displayed + onNodeWithTag(TestTags.DICE_ROLL_DIALOG).assertIsDisplayed() + + // Verify dice count text + onNodeWithTag(TestTags.DICE_ROLL_DICE_COUNT).assertTextContains("6 dice rolled") + + // Verify success count text + onNodeWithTag(TestTags.DICE_ROLL_SUCCESS_COUNT).assertTextContains("3 successes") + } + + @Test + fun dialogShowsIndividualDieChips() = runComposeUiTest { + val roll = testDiceRoll() + setContent { + DiceRollResultDialog( + diceRoll = roll, + rollLabel = "Agility", + onDismiss = {} + ) + } + + // Verify each die chip is displayed + for (i in roll.result.indices) { + onNodeWithTag(TestTags.dieChip(i)).assertIsDisplayed() + } + } + + @Test + fun dialogShowsRollLabel() = runComposeUiTest { + setContent { + DiceRollResultDialog( + diceRoll = testDiceRoll(), + rollLabel = "Body", + onDismiss = {} + ) + } + + // Verify the roll label appears as title + onNodeWithText("Body").assertIsDisplayed() + } + + @Test + fun dialogCanBeDismissedViaOkButton() = runComposeUiTest { + var dismissed = false + + setContent { + var showDialog by remember { mutableStateOf(true) } + + if (showDialog) { + DiceRollResultDialog( + diceRoll = testDiceRoll(), + rollLabel = "Body", + onDismiss = { + dismissed = true + showDialog = false + } + ) + } + } + + // Dialog should be visible initially + onNodeWithTag(TestTags.DICE_ROLL_DIALOG).assertIsDisplayed() + + // Click dismiss button + onNodeWithTag(TestTags.DICE_ROLL_DISMISS_BUTTON).performClick() + + // Verify dismiss callback was triggered + assertTrue(dismissed, "onDismiss callback should have been called") + + // Dialog should no longer exist + val nodes = onAllNodesWithTag(TestTags.DICE_ROLL_DIALOG).fetchSemanticsNodes() + assertTrue(nodes.isEmpty(), "Dialog should be gone after dismiss") + } +} diff --git a/sharedUI/src/commonTest/kotlin/org/shahondin1624/TestTagsTest.kt b/sharedUI/src/commonTest/kotlin/org/shahondin1624/TestTagsTest.kt index 1996155..832e0b8 100644 --- a/sharedUI/src/commonTest/kotlin/org/shahondin1624/TestTagsTest.kt +++ b/sharedUI/src/commonTest/kotlin/org/shahondin1624/TestTagsTest.kt @@ -42,6 +42,16 @@ class TestTagsTest { assertTrue(TestTags.PANEL_DAMAGE_MONITOR.isNotBlank()) } + @Test + fun diceRollDialogTagsAreDefined() { + assertTrue(TestTags.DICE_ROLL_DIALOG.isNotBlank()) + assertTrue(TestTags.DICE_ROLL_DICE_COUNT.isNotBlank()) + assertTrue(TestTags.DICE_ROLL_SUCCESS_COUNT.isNotBlank()) + assertTrue(TestTags.DICE_ROLL_DISMISS_BUTTON.isNotBlank()) + assertEquals("die_chip_0", TestTags.dieChip(0)) + assertEquals("die_chip_5", TestTags.dieChip(5)) + } + @Test fun allTagsAreUnique() { val staticTags = listOf( @@ -55,7 +65,11 @@ class TestTagsTest { TestTags.NAV_PERMANENT_DRAWER, TestTags.TOP_APP_BAR, TestTags.THEME_TOGGLE, - TestTags.MENU_BUTTON + TestTags.MENU_BUTTON, + TestTags.DICE_ROLL_DIALOG, + TestTags.DICE_ROLL_DICE_COUNT, + TestTags.DICE_ROLL_SUCCESS_COUNT, + TestTags.DICE_ROLL_DISMISS_BUTTON ) assertEquals(staticTags.size, staticTags.toSet().size, "All static tags should be unique") }