test(#293): add foundational Compose UI test for StateViews (TEST-15) (#301)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-06-22 05:25:28 +03:00
1 parent 60bf6e8a33
commit bbe3b00ccb
1 file changed
+116
@@ -0,0 +1,116 @@
package com.m57.hermescontrol.ui.common
import androidx.compose.ui.test.assertHasClickAction
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented Compose UI tests for [LoadingState], [ErrorState], and [EmptyState].
*
* This is the **first Compose UI test in the repo** (TEST-15, issue #293).
* It establishes the testing pattern for future composable tests.
*/
@RunWith(AndroidJUnit4::class)
@MediumTest
class StateViewsTest {
@get:Rule
val composeTestRule = createComposeRule()
// ── LoadingState ────────────────────────────────────────────────────
@Test
fun loadingState_displaysCenteredSpinner() {
composeTestRule.setContent { LoadingState() }
composeTestRule.onNodeWithTag("loading_state").assertIsDisplayed()
composeTestRule.onNodeWithTag("loading_spinner").assertIsDisplayed()
}
// ── ErrorState ──────────────────────────────────────────────────────
@Test
fun errorState_displaysMessage() {
val message = "Something went wrong"
composeTestRule.setContent { ErrorState(message = message) }
composeTestRule.onNodeWithText(message).assertIsDisplayed()
}
@Test
fun errorState_withRetry_showsRetryButton() {
var retryClicked = false
composeTestRule.setContent {
ErrorState(
message = "Try again",
onRetry = { retryClicked = true },
)
}
composeTestRule.onNodeWithTag("error_retry_button").assertIsDisplayed()
composeTestRule.onNodeWithTag("error_retry_button").assertHasClickAction()
composeTestRule.onNodeWithTag("error_retry_button").performClick()
assert(retryClicked) { "Retry callback was not invoked on click" }
}
@Test
fun errorState_withoutRetry_hidesRetryButton() {
composeTestRule.setContent { ErrorState(message = "No retry") }
composeTestRule.onNodeWithTag("error_retry_button").assertDoesNotExist()
}
// ── EmptyState ──────────────────────────────────────────────────────
@Test
fun emptyState_displaysTitle() {
val title = "No items"
composeTestRule.setContent { EmptyState(title = title) }
composeTestRule.onNodeWithText(title).assertIsDisplayed()
}
@Test
fun emptyState_withSubtitle_displaysSubtitle() {
composeTestRule.setContent { EmptyState(title = "Empty", subtitle = "Add something") }
composeTestRule.onNodeWithText("Add something").assertIsDisplayed()
}
@Test
fun emptyState_withoutSubtitle_doesNotShowSubtitle() {
composeTestRule.setContent { EmptyState(title = "Empty") }
composeTestRule.onNodeWithText("Add something").assertDoesNotExist()
}
@Test
fun emptyState_withAction_showsActionButton() {
var actionClicked = false
composeTestRule.setContent {
EmptyState(
title = "No data",
actionLabel = "Create",
onAction = { actionClicked = true },
)
}
composeTestRule.onNodeWithTag("empty_state_action").assertIsDisplayed()
composeTestRule.onNodeWithTag("empty_state_action").assertHasClickAction()
composeTestRule.onNodeWithTag("empty_state_action").performClick()
assert(actionClicked) { "Action callback was not invoked on click" }
}
@Test
fun emptyState_withoutAction_hidesActionButton() {
composeTestRule.setContent { EmptyState(title = "Just text") }
composeTestRule.onNodeWithTag("empty_state_action").assertDoesNotExist()
}
}