Skip to content

Commit

Permalink
Feature/66 unmerged tree cfg (#75)
Browse files Browse the repository at this point in the history
* Support useUnmergedTree config param

* 2.5.0-alpha07
  • Loading branch information
alex-tiurin authored Jul 4, 2024
1 parent a894505 commit ccb2a67
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/android-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
java-version: '17'

- name: Compile framework
run: ./gradlew compileDebugKotlin
run: ./gradlew compileDebugKotlin compileDebugKotlinAndroid compileKotlinDesktop compileKotlinJvm compileKotlinIosArm64 compileKotlinIosSimulatorArm64 compileKotlinJs compileKotlinWasmJs
61 changes: 61 additions & 0 deletions composeApp/src/commonTest/kotlin/BaseInteractionTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import com.atiurin.ultron.core.common.options.TextContainsOption
import com.atiurin.ultron.core.compose.config.UltronComposeConfig
import com.atiurin.ultron.core.compose.nodeinteraction.click
import com.atiurin.ultron.core.compose.runUltronUiTest
import com.atiurin.ultron.extensions.assertIsDisplayed
import com.atiurin.ultron.extensions.assertTextContains
import com.atiurin.ultron.extensions.isSuccess
import com.atiurin.ultron.extensions.withAssertion
import com.atiurin.ultron.extensions.withUseUnmergedTree
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

@OptIn(ExperimentalTestApi::class)
class BaseInteractionTest {
@Test
fun test() = runUltronUiTest {
setContent {
App()
}
hasText("Click me!").withAssertion() {
hasTestTag("greeting")
.assertIsDisplayed()
.assertTextContains("Compose: Hello,", option = TextContainsOption(substring = true))
}.click()
}

@Test
fun useUnmergedTreeConfigTest() = runUltronUiTest {
val testTag = "element"
setContent {
Column {
Button(onClick = {}, modifier = Modifier.testTag(testTag)) {
Text("Text1")
Text("Text2")
}
}
}
UltronComposeConfig.params.useUnmergedTree = true
assertFalse("Ultron operation success should be false") {
hasTestTag(testTag).isSuccess { assertTextContains("Text1") }
}
assertTrue ("Ultron operation success should be true") {
hasTestTag(testTag).withUseUnmergedTree(false).isSuccess { assertTextContains("Text1") }
}
}

@AfterTest
fun disableUseUnmergedTree(){
UltronComposeConfig.params.useUnmergedTree = false
}
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.hasText
import com.atiurin.ultron.core.common.options.TextContainsOption
import com.atiurin.ultron.core.compose.list.UltronComposeListItem
import com.atiurin.ultron.core.compose.list.composeList
import com.atiurin.ultron.core.compose.nodeinteraction.click
import com.atiurin.ultron.core.compose.runUltronUiTest
import com.atiurin.ultron.extensions.assertIsDisplayed
import com.atiurin.ultron.extensions.withAssertion
import com.atiurin.ultron.page.Screen
import repositories.ContactRepository
import kotlin.test.Test

@OptIn(ExperimentalTestApi::class)
class AppTest {
@Test
fun test() = runUltronUiTest {
setContent {
App()
}
hasText("Click me!").withAssertion() {
hasTestTag("greeting")
.assertIsDisplayed()
.assertTextContains("Compose: Hello,", option = TextContainsOption(substring = true))
}.click()
}
class ListTest {

@Test
fun testList() = runUltronUiTest {
Expand Down
3 changes: 3 additions & 0 deletions composeApp/src/jsMain/kotlin/Platform.js.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
actual fun getPlatform(): Platform {
TODO("Not yet implemented")
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ kotlin.mpp.enableCInteropCommonization=true

GROUP=com.atiurin
POM_ARTIFACT_ID=ultron
VERSION_NAME=2.5.0-alpha06
VERSION_NAME=2.5.0-alpha07
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class ComposeListTest : BaseTest() {
val contact = CONTACTS[index]
listWithMergedTree.visibleItem(index).printToLog("ULTRON")
.assertMatches(hasAnyDescendant(hasText(contact.name)))

}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ class UltronComposeList(
val listMatcher: SemanticsMatcher,
var useUnmergedTree: Boolean = true,
var positionPropertyKey: SemanticsPropertyKey<Int>? = null,
val itemsRegistrator: UltronComposeList.() -> Unit = {},
val initBlock: UltronComposeList.() -> Unit = {},
private val itemSearchLimit: Int = UltronComposeConfig.params.lazyColumnItemSearchLimit,
private var operationTimeoutMs: Long = UltronComposeConfig.params.lazyColumnOperationTimeoutMs
) {
private val itemChildInteractionProvider = getItemChildInteractionProvider()
val instancesMap = mutableMapOf<KClass<*>, () -> UltronComposeListItem>()
val itemInstancesMap = mutableMapOf<KClass<*>, () -> UltronComposeListItem>()
inline fun <reified T : UltronComposeListItem> registerItem(noinline creator: () -> T){
instancesMap[T::class] = creator
itemInstancesMap[T::class] = creator
}
init {
itemsRegistrator()
initBlock()
}

open fun withTimeout(timeoutMs: Long) =
Expand Down Expand Up @@ -224,15 +224,15 @@ class UltronComposeList(
fun scrollToNode(itemMatcher: SemanticsMatcher) = apply { getInteraction().scrollToNode(itemMatcher) }
fun scrollToIndex(index: Int) = apply { getInteraction().scrollToIndex(index) }
fun scrollToKey(key: Any) = apply { getInteraction().scrollToKey(key) }
fun assertIsDisplayed() = apply { getInteraction().withTimeout(getOperationTimeout()).assertIsDisplayed() }
fun assertIsNotDisplayed() = apply { getInteraction().withTimeout(getOperationTimeout()).assertIsNotDisplayed() }
fun assertExists() = apply { getInteraction().withTimeout(getOperationTimeout()).assertExists() }
fun assertDoesNotExist() = apply { getInteraction().withTimeout(getOperationTimeout()).assertDoesNotExist() }
fun assertContentDescriptionEquals(vararg expected: String) = apply { getInteraction().withTimeout(getOperationTimeout()).assertContentDescriptionEquals(*expected) }
fun assertIsDisplayed() = apply { getInteraction().assertIsDisplayed() }
fun assertIsNotDisplayed() = apply { getInteraction().assertIsNotDisplayed() }
fun assertExists() = apply { getInteraction().assertExists() }
fun assertDoesNotExist() = apply { getInteraction().assertDoesNotExist() }
fun assertContentDescriptionEquals(vararg expected: String) = apply { getInteraction().assertContentDescriptionEquals(*expected) }
fun assertContentDescriptionContains(expected: String, option: ContentDescriptionContainsOption? = null) =
apply { getInteraction().withTimeout(getOperationTimeout()).assertContentDescriptionContains(expected, option) }
apply { getInteraction().assertContentDescriptionContains(expected, option) }

fun assertMatches(matcher: SemanticsMatcher) = apply { getInteraction().withTimeout(getOperationTimeout()).assertMatches(matcher) }
fun assertMatches(matcher: SemanticsMatcher) = apply { getInteraction().assertMatches(matcher) }
fun assertNotEmpty() = apply {
AssertUtils.assertTrue(
{ getVisibleItemsCount() > 0 }, getOperationTimeout(),
Expand Down Expand Up @@ -261,7 +261,7 @@ class UltronComposeList(
* Otherwise, an exception will be thrown.
*/
fun assertItemDoesNotExist(itemMatcher: SemanticsMatcher) {
getInteraction().withTimeout(getOperationTimeout()).perform(
getInteraction().perform(
params = UltronComposeOperationParams(
operationName = "Assert item ${itemMatcher.description} doesn't exist in list ${getInteraction().elementInfo.name}",
operationDescription = "Assert item ${itemMatcher.description} doesn't exist in list ${getInteraction().elementInfo.name} during ${getOperationTimeout()}",
Expand All @@ -281,7 +281,7 @@ class UltronComposeList(
}

fun getVisibleItemsCount(): Int = getInteraction().execute { it.fetchSemanticsNode().children.size }
fun getInteraction() = UltronComposeSemanticsNodeInteraction(listMatcher, useUnmergedTree)
fun getInteraction() = UltronComposeSemanticsNodeInteraction(listMatcher, useUnmergedTree, operationTimeoutMs)

@Deprecated("Use getInteraction() instead", ReplaceWith("getInteraction()"))
fun getMatcher() = getInteraction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ open class UltronComposeSemanticsNodeInteraction constructor(
) {
constructor(
matcher: SemanticsMatcher,
useUnmergedTree: Boolean = false,
useUnmergedTree: Boolean = UltronComposeConfig.params.useUnmergedTree,
timeoutMs: Long = UltronComposeConfig.params.operationTimeoutMs,
resultHandler: ((ComposeOperationResult<UltronComposeOperation>) -> Unit) = UltronComposeConfig.resultHandler,
assertion: OperationAssertion = EmptyOperationAssertion(),
elementInfo: ElementInfo = DefaultElementInfo()
) : this(SemanticsNodeInteractionProviderContainer.getProvider().onNode(matcher, useUnmergedTree), timeoutMs, resultHandler, assertion, elementInfo)

init {
if (elementInfo.name.isEmpty()) elementInfo.name = semanticsNodeInteraction.getSelectorDescription().toString()
if (elementInfo.name.isEmpty()) elementInfo.name = semanticsNodeInteraction.getSelectorDescription()
}

fun <T> isSuccess(action: UltronComposeSemanticsNodeInteraction.() -> T): Boolean = runCatching { action() }.isSuccess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
ultronComposeList: UltronComposeList,
itemMatcher: SemanticsMatcher
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand All @@ -28,7 +28,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
position: Int,
isPositionPropertyConfigured: Boolean
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
ultronComposeList: UltronComposeList,
itemMatcher: SemanticsMatcher
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand All @@ -28,7 +28,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
position: Int,
isPositionPropertyConfigured: Boolean
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
ultronComposeList: UltronComposeList,
itemMatcher: SemanticsMatcher
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand All @@ -28,7 +28,7 @@ actual inline fun <reified T : UltronComposeListItem> getComposeListItemInstance
position: Int,
isPositionPropertyConfigured: Boolean
): T {
val item = ultronComposeList.instancesMap[T::class]?.invoke()
val item = ultronComposeList.itemInstancesMap[T::class]?.invoke()
?: throw UltronException(
"""
|Item with ${T::class} not registered in compose list ${ultronComposeList.listMatcher.description}
Expand Down

0 comments on commit ccb2a67

Please sign in to comment.