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