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>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user