From e042120999010557a35ca14467dd3c8945633bb0 Mon Sep 17 00:00:00 2001 From: dabrynskiy Date: Mon, 5 May 2025 22:28:15 +0300 Subject: [PATCH] New logic for atPositionItemChild, atItemChild --- .../ultron/core/config/UltronConfig.kt | 1 + .../ultron/core/config/UltronRecyclerImpl.kt | 6 +++ .../recyclerview/UltronRecyclerView.kt | 49 ++++++++++++++---- .../recyclerviewv2/UltronRecyclerViewV2.kt | 50 +++++++++++++++---- 4 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronRecyclerImpl.kt diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronConfig.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronConfig.kt index de48c944..40a71dfd 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronConfig.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronConfig.kt @@ -147,6 +147,7 @@ object UltronConfig { var RECYCLER_VIEW_OPERATIONS_TIMEOUT = DEFAULT_RECYCLER_VIEW_OPERATION_TIMEOUT var RECYCLER_VIEW_ITEM_SEARCH_LIMIT = -1 var INCLUDE_VIEW_HIERARCHY_TO_EXCEPTION = false //where it applicable + var RECYCLER_IMPL = UltronRecyclerImpl.STANDART var resultAnalyzer: OperationResultAnalyzer = UltronCommonConfig.resultAnalyzer diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronRecyclerImpl.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronRecyclerImpl.kt new file mode 100644 index 00000000..c53e9810 --- /dev/null +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/config/UltronRecyclerImpl.kt @@ -0,0 +1,6 @@ +package com.atiurin.ultron.core.config + +enum class UltronRecyclerImpl { + STANDART, + PERFORMANCE +} diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerview/UltronRecyclerView.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerview/UltronRecyclerView.kt index 7717d32c..0b849f77 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerview/UltronRecyclerView.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerview/UltronRecyclerView.kt @@ -9,11 +9,14 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.contrib.RecyclerViewActions import androidx.test.espresso.matcher.BoundedMatcher +import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.util.TreeIterables +import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_IMPL import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_ITEM_SEARCH_LIMIT import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_LOAD_TIMEOUT import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_OPERATIONS_TIMEOUT +import com.atiurin.ultron.core.config.UltronRecyclerImpl import com.atiurin.ultron.core.espresso.EspressoOperationResult import com.atiurin.ultron.core.espresso.UltronEspressoInteraction import com.atiurin.ultron.core.espresso.UltronEspressoOperation @@ -32,12 +35,14 @@ import org.hamcrest.TypeSafeMatcher * @param itemSearchLimit set an amount of RecyclerView items to be researched for target item. There is no limit by default * [itemSearchLimit] is applied for matcher search. If you're looking for RecyclerView item by position it isn't used. * @param operationTimeoutMs specifies a timeout for actions and assertions on [UltronRecyclerView]. [UltronRecyclerViewItem] has it own timeout. + * @param recyclerImpl specifies a recycler item child matcher implementation */ open class UltronRecyclerView( val recyclerViewMatcher: Matcher, val loadTimeoutMs: Long = RECYCLER_VIEW_LOAD_TIMEOUT, private val itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - private var operationTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + private var operationTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + private val recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ) { private var recyclerView: RecyclerView? = null @@ -550,10 +555,20 @@ open class UltronRecyclerView( } override fun matchesSafely(view: View): Boolean { - findItemView(itemMatcher, view.rootView)?.itemView?.let { - childView = it.findChildView(childMatcher) + return when(recyclerImpl) { + UltronRecyclerImpl.STANDART -> { + findItemView(itemMatcher, view.rootView)?.itemView?.let { + childView = it.findChildView(childMatcher) + } + if (childView != null) childView == view else false + } + + UltronRecyclerImpl.PERFORMANCE -> { + if (childMatcher.matches(view)) { + isDescendantOfA(atItem(itemMatcher)).matches(view) + } else false + } } - return if (childView != null) childView == view else false } } } @@ -571,10 +586,20 @@ open class UltronRecyclerView( } override fun matchesSafely(view: View): Boolean { - findItemViewAtPosition(position, view.rootView)?.itemView.let { - childView = it?.findChildView(childMatcher) + return when(recyclerImpl) { + UltronRecyclerImpl.STANDART -> { + findItemViewAtPosition(position, view.rootView)?.itemView.let { + childView = it?.findChildView(childMatcher) + } + return if (childView != null) childView == view else false + } + + UltronRecyclerImpl.PERFORMANCE -> { + if (childMatcher.matches(view)) { + isDescendantOfA(atPosition(position)).matches(view) + } else false + } } - return if (childView != null) childView == view else false } } } @@ -615,18 +640,20 @@ fun withRecyclerView( recyclerViewMatcher: Matcher, loadTimeout: Long = RECYCLER_VIEW_LOAD_TIMEOUT, itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ): UltronRecyclerView { - return UltronRecyclerView(recyclerViewMatcher, loadTimeout, itemSearchLimit, operationsTimeoutMs) + return UltronRecyclerView(recyclerViewMatcher, loadTimeout, itemSearchLimit, operationsTimeoutMs, recyclerImpl) } fun withRecyclerView( @IntegerRes resourceId: Int, loadTimeout: Long = RECYCLER_VIEW_LOAD_TIMEOUT, itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ): UltronRecyclerView { - return UltronRecyclerView(withId(resourceId), loadTimeout, itemSearchLimit, operationsTimeoutMs) + return UltronRecyclerView(withId(resourceId), loadTimeout, itemSearchLimit, operationsTimeoutMs, recyclerImpl) } private fun View.findChildView(matcher: Matcher): View? { diff --git a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerviewv2/UltronRecyclerViewV2.kt b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerviewv2/UltronRecyclerViewV2.kt index b0139098..f4250933 100644 --- a/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerviewv2/UltronRecyclerViewV2.kt +++ b/ultron-android/src/main/kotlin/com/atiurin/ultron/core/espresso/recyclerviewv2/UltronRecyclerViewV2.kt @@ -9,13 +9,16 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.BoundedMatcher import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom +import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.util.HumanReadables import androidx.test.espresso.util.TreeIterables +import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_IMPL import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_ITEM_SEARCH_LIMIT import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_LOAD_TIMEOUT import com.atiurin.ultron.core.config.UltronConfig.Espresso.Companion.RECYCLER_VIEW_OPERATIONS_TIMEOUT +import com.atiurin.ultron.core.config.UltronRecyclerImpl import com.atiurin.ultron.core.espresso.EspressoOperationResult import com.atiurin.ultron.core.espresso.UltronEspressoInteraction import com.atiurin.ultron.core.espresso.UltronEspressoOperation @@ -40,7 +43,8 @@ open class UltronRecyclerViewV2( val recyclerViewMatcher: Matcher, val loadTimeoutMs: Long = RECYCLER_VIEW_LOAD_TIMEOUT, private val itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - private var operationTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + private var operationTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + private val recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ) { private var recyclerView: RecyclerView? = null @@ -606,10 +610,20 @@ open class UltronRecyclerViewV2( } override fun matchesSafely(view: View): Boolean { - findItemView(itemMatcher, view.rootView)?.itemView?.let { - childView = it.findChildView(childMatcher) + return when(recyclerImpl) { + UltronRecyclerImpl.STANDART -> { + findItemView(itemMatcher, view.rootView)?.itemView?.let { + childView = it.findChildView(childMatcher) + } + return if (childView != null) childView == view else false + } + + UltronRecyclerImpl.PERFORMANCE -> { + if (childMatcher.matches(view)) { + isDescendantOfA(atItem(itemMatcher)).matches(view) + } else false + } } - return if (childView != null) childView == view else false } } } @@ -628,10 +642,20 @@ open class UltronRecyclerViewV2( } override fun matchesSafely(view: View): Boolean { - findItemViewAtPosition(position, view.rootView)?.itemView.let { - childView = it?.findChildView(childMatcher) + return when(recyclerImpl) { + UltronRecyclerImpl.STANDART -> { + findItemViewAtPosition(position, view.rootView)?.itemView.let { + childView = it?.findChildView(childMatcher) + } + return if (childView != null) childView == view else false + } + + UltronRecyclerImpl.PERFORMANCE -> { + if (childMatcher.matches(view)) { + isDescendantOfA(atPosition(position)).matches(view) + } else false + } } - return if (childView != null) childView == view else false } } } @@ -682,13 +706,15 @@ fun withRecyclerViewV2( recyclerViewMatcher: Matcher, loadTimeout: Long = RECYCLER_VIEW_LOAD_TIMEOUT, itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ): UltronRecyclerViewV2 { return UltronRecyclerViewV2( recyclerViewMatcher, loadTimeout, itemSearchLimit, - operationsTimeoutMs + operationsTimeoutMs, + recyclerImpl ) } @@ -696,13 +722,15 @@ fun withRecyclerViewV2( @IntegerRes resourceId: Int, loadTimeout: Long = RECYCLER_VIEW_LOAD_TIMEOUT, itemSearchLimit: Int = RECYCLER_VIEW_ITEM_SEARCH_LIMIT, - operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT + operationsTimeoutMs: Long = RECYCLER_VIEW_OPERATIONS_TIMEOUT, + recyclerImpl: UltronRecyclerImpl = RECYCLER_IMPL ): UltronRecyclerViewV2 { return UltronRecyclerViewV2( withId(resourceId), loadTimeout, itemSearchLimit, - operationsTimeoutMs + operationsTimeoutMs, + recyclerImpl ) }