Files
honeyDueKMP/composeApp/src/androidInstrumentedTest/kotlin/com/tt/honeyDue/ui/BaseScreen.kt
Trey T 2d80ade6bc Test infra: shared accessibility IDs + PageObjects + AAA_SeedTests
Ports iOS HoneyDueUITests AccessibilityIdentifiers + PageObjects pattern
to Android Compose UI Test. Kotlin AccessibilityIds object mirrors Swift
verbatim so scripts/verify_test_tag_parity.sh can gate on divergence.

AAA_SeedTests bracketed first alphanumerically; SuiteZZ cleanup to follow.

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

86 lines
2.8 KiB
Kotlin

package com.tt.honeyDue.ui
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
/**
* Base class for Android Compose UI test page objects.
*
* Mirrors `iosApp/HoneyDueUITests/PageObjects/BaseScreen.swift`: provides
* condition-based waits so screen objects can interact with Compose nodes
* without reaching for ad-hoc `Thread.sleep` calls.
*/
abstract class BaseScreen(protected val rule: ComposeTestRule) {
/** Returns a node interaction for the given test tag (unmerged tree for testability). */
protected fun tag(testTag: String): SemanticsNodeInteraction =
rule.onNodeWithTag(testTag, useUnmergedTree = true)
/** Returns a node interaction for the given display text. */
protected fun text(value: String): SemanticsNodeInteraction =
rule.onNodeWithText(value, useUnmergedTree = true)
/** Waits until a node with [testTag] exists in the semantics tree. */
protected fun waitFor(testTag: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
rule.waitUntil(timeoutMs) {
try {
tag(testTag).assertExists()
true
} catch (e: AssertionError) {
false
}
}
}
/** Waits until a node with the given visible [value] text exists. */
protected fun waitForText(value: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
rule.waitUntil(timeoutMs) {
try {
text(value).assertExists()
true
} catch (e: AssertionError) {
false
}
}
}
/** Waits until a node with [testTag] is actually displayed (not just present). */
protected fun waitForDisplayed(testTag: String, timeoutMs: Long = DEFAULT_TIMEOUT_MS) {
rule.waitUntil(timeoutMs) {
try {
tag(testTag).assertIsDisplayed()
true
} catch (e: AssertionError) {
false
}
}
}
/** Non-throwing existence check. */
protected fun exists(testTag: String): Boolean = try {
tag(testTag).assertExists()
true
} catch (e: AssertionError) {
false
}
/** Non-throwing text existence check. */
protected fun textExists(value: String): Boolean = try {
text(value).assertExists()
true
} catch (e: AssertionError) {
false
}
/** Subclasses report whether their screen is currently visible. */
abstract fun isDisplayed(): Boolean
companion object {
const val DEFAULT_TIMEOUT_MS: Long = 10_000L
const val SHORT_TIMEOUT_MS: Long = 5_000L
}
}