Files
honeyDueKMP/composeApp/src/androidInstrumentedTest/kotlin/com/tt/honeyDue/SimpleLoginTest.kt
Trey T 95dabf741f UI Test Suite1: Registration + SimpleLogin ports (iOS parity)
Ports iOS Suite1_RegistrationTests.swift + SimpleLoginTest.swift to
Android Compose UI Test. Adds testTag annotations on auth screens using
shared AccessibilityIds.Authentication constants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:38:56 -05:00

105 lines
3.7 KiB
Kotlin

package com.tt.honeyDue
import android.content.Context
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performTextInput
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.tt.honeyDue.data.DataManager
import com.tt.honeyDue.data.PersistenceManager
import com.tt.honeyDue.storage.TaskCacheManager
import com.tt.honeyDue.storage.TaskCacheStorage
import com.tt.honeyDue.storage.ThemeStorageManager
import com.tt.honeyDue.storage.TokenManager
import com.tt.honeyDue.testing.AccessibilityIds
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Android port of `iosApp/HoneyDueUITests/SimpleLoginTest.swift` — a smoke
* test suite that verifies the app launches and surfaces a usable login
* screen. Merged into one test (`testAppLaunchesAndShowsLoginScreen`) because
* `createAndroidComposeRule<MainActivity>()` launches a fresh activity per
* test anyway, and the two iOS tests exercise the exact same semantic
* contract.
*/
@RunWith(AndroidJUnit4::class)
class SimpleLoginTest {
@get:Rule
val composeRule = createAndroidComposeRule<MainActivity>()
@Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>()
if (!isDataManagerInitialized()) {
DataManager.initialize(
tokenMgr = TokenManager.getInstance(context),
themeMgr = ThemeStorageManager.getInstance(context),
persistenceMgr = PersistenceManager.getInstance(context),
)
}
TaskCacheStorage.initialize(TaskCacheManager.getInstance(context))
// CRITICAL: mirror iOS `ensureLoggedOut()` — UITestHelpers handles both
// the already-logged-in and mid-onboarding cases.
UITestHelpers.ensureOnLoginScreen(composeRule)
}
@After
fun tearDown() {
UITestHelpers.tearDown(composeRule)
}
/**
* iOS: `testAppLaunchesAndShowsLoginScreen` + `testCanTypeInLoginFields`.
*
* Verifies the login screen elements exist AND that the username/password
* fields accept text input (the minimum contract for SimpleLoginTest).
*/
@Test
fun testAppLaunchesAndShowsLoginScreen() {
// App launches and username field is reachable.
composeRule.onNodeWithTag(
AccessibilityIds.Authentication.usernameField,
useUnmergedTree = true,
).assertIsDisplayed()
// Can type into username & password fields.
composeRule.onNodeWithTag(
AccessibilityIds.Authentication.usernameField,
useUnmergedTree = true,
).performTextInput("testuser")
composeRule.onNodeWithTag(
AccessibilityIds.Authentication.passwordField,
useUnmergedTree = true,
).performTextInput("testpass123")
// Login button should be displayed (and, because both fields are
// populated, also enabled — we don't tap it here to avoid a real API
// call from a smoke test).
composeRule.onNodeWithTag(
AccessibilityIds.Authentication.loginButton,
useUnmergedTree = true,
).assertIsDisplayed()
}
private fun isDataManagerInitialized(): Boolean {
return try {
val field = DataManager::class.java.getDeclaredField("_isInitialized")
field.isAccessible = true
@Suppress("UNCHECKED_CAST")
val flow = field.get(DataManager) as kotlinx.coroutines.flow.MutableStateFlow<Boolean>
flow.value
} catch (t: Throwable) {
false
}
}
}