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>
86 lines
2.8 KiB
Kotlin
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
|
|
}
|
|
}
|