diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/autoscroll/ObjectAutoScrollProviderImpl.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/autoscroll/ObjectAutoScrollProviderImpl.kt index 6d4487aca..0a33ec252 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/autoscroll/ObjectAutoScrollProviderImpl.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/autoscroll/ObjectAutoScrollProviderImpl.kt @@ -1,5 +1,6 @@ package com.kaspersky.kaspresso.autoscroll +import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.UiScrollable import androidx.test.uiautomator.UiSelector import com.kaspersky.components.kautomator.intercept.interaction.UiObjectInteraction @@ -27,6 +28,7 @@ class ObjectAutoScrollProviderImpl( */ override fun withAutoScroll(interaction: UiObjectInteraction, action: () -> T): T { return try { + interaction.findUiObject() action.invoke() } catch (error: Throwable) { if (error.isAllowed(autoScrollParams.allowedExceptions) && UiScrollable(UiSelector().scrollable(true)).exists()) { @@ -56,7 +58,7 @@ class ObjectAutoScrollProviderImpl( * Scrolls to the bottom and looks for the given view. Invokes the action if the view was found. */ do { - if (interaction.uiObject2 != null) { + if (interaction.findUiObject() != null) { logger.i("UiObject autoscroll to the bottom successfully performed.") return action.invoke() } else { @@ -68,7 +70,7 @@ class ObjectAutoScrollProviderImpl( * Scrolls to the beginning and looks for the given view. Invokes the action if the view was found. */ do { - if (interaction.uiObject2 != null) { + if (interaction.findUiObject() != null) { logger.i("UiObject autoscroll to the beginning successfully performed.") return action.invoke() } else { @@ -79,4 +81,12 @@ class ObjectAutoScrollProviderImpl( logger.i("UiObject autoscroll did not help. Throwing exception.") throw cachedError } + + private fun UiObjectInteraction.findUiObject(): UiObject2? { + if (uiObject2 != null) { + return uiObject2 + } + reFindUiObject() + return null + } } diff --git a/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/common/actions/UiScrollableActions.kt b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/common/actions/UiScrollableActions.kt index dcb9be7ca..4fae57faa 100644 --- a/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/common/actions/UiScrollableActions.kt +++ b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/common/actions/UiScrollableActions.kt @@ -8,10 +8,12 @@ import com.kaspersky.components.kautomator.intercept.operation.UiOperationType interface UiScrollableActions : UiBaseActions { + val setUiScrollableOrientation: UiScrollable.() -> UiScrollable + fun scrollToStart() { view.perform(UiScrollableActionType.SCROLL_TO_START) { val scrollable = UiScrollable(UiSelector().resourceId(resourceName)) - scrollable.setAsVerticalList() + scrollable.setUiScrollableOrientation() scrollable.flingToBeginning(Int.MAX_VALUE) } } @@ -19,7 +21,7 @@ interface UiScrollableActions : UiBaseActions { fun scrollToEnd() { view.perform(UiScrollableActionType.SCROLL_TO_END) { val scrollable = UiScrollable(UiSelector().resourceId(resourceName)) - scrollable.setAsVerticalList() + scrollable.setUiScrollableOrientation() scrollable.flingToEnd(Int.MAX_VALUE) } } @@ -27,13 +29,15 @@ interface UiScrollableActions : UiBaseActions { fun scrollToView(to: UiBaseView) { view.perform(UiScrollableActionType.SCROLL_TO_VIEW) { val scrollable = UiScrollable(UiSelector().resourceId(resourceName)) + scrollable.setUiScrollableOrientation() do { - if (findObject(to.view.interaction.selector.bySelector) != null) + if (findObject(to.view.interaction.selector.bySelector) != null) { return@perform + } } while (scrollable.scrollForward()) do { if (findObject(to.view.interaction.selector.bySelector) != null) - return@perform + return@perform } while (scrollable.scrollBackward()) to.isDisplayed() } diff --git a/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiHorizontalScrollView.kt b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiHorizontalScrollView.kt new file mode 100644 index 000000000..a36137463 --- /dev/null +++ b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiHorizontalScrollView.kt @@ -0,0 +1,17 @@ +@file:Suppress("unused") +package com.kaspersky.components.kautomator.component.scroll + +import androidx.test.uiautomator.UiScrollable +import com.kaspersky.components.kautomator.component.common.actions.UiScrollableActions +import com.kaspersky.components.kautomator.component.common.actions.UiSwipeableActions +import com.kaspersky.components.kautomator.component.common.builders.UiViewBuilder +import com.kaspersky.components.kautomator.component.common.builders.UiViewSelector +import com.kaspersky.components.kautomator.component.common.views.UiBaseView + +class UiHorizontalScrollView : UiBaseView, UiSwipeableActions, UiScrollableActions { + constructor(selector: UiViewSelector) : super(selector) + constructor(builder: UiViewBuilder.() -> Unit) : super(builder) + + override val setUiScrollableOrientation: UiScrollable.() -> UiScrollable + get() = { setAsHorizontalList() } +} diff --git a/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiScrollView.kt b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiScrollView.kt index e82c66967..1fa8e2320 100644 --- a/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiScrollView.kt +++ b/kautomator/src/main/kotlin/com/kaspersky/components/kautomator/component/scroll/UiScrollView.kt @@ -1,6 +1,8 @@ @file:Suppress("unused") + package com.kaspersky.components.kautomator.component.scroll +import androidx.test.uiautomator.UiScrollable import com.kaspersky.components.kautomator.component.common.actions.UiScrollableActions import com.kaspersky.components.kautomator.component.common.actions.UiSwipeableActions import com.kaspersky.components.kautomator.component.common.builders.UiViewBuilder @@ -10,4 +12,7 @@ import com.kaspersky.components.kautomator.component.common.views.UiBaseView class UiScrollView : UiBaseView, UiSwipeableActions, UiScrollableActions { constructor(selector: UiViewSelector) : super(selector) constructor(builder: UiViewBuilder.() -> Unit) : super(builder) + + override val setUiScrollableOrientation: UiScrollable.() -> UiScrollable + get() = { setAsVerticalList() } } diff --git a/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/screen/NestedScrollViewsScreen.kt b/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/screen/NestedScrollViewsScreen.kt new file mode 100644 index 000000000..d8bdd1d52 --- /dev/null +++ b/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/screen/NestedScrollViewsScreen.kt @@ -0,0 +1,18 @@ +package com.kaspersky.kaspresso.kautomatorsample.screen + +import com.kaspersky.components.kautomator.component.scroll.UiHorizontalScrollView +import com.kaspersky.components.kautomator.component.text.UiButton +import com.kaspersky.components.kautomator.screen.UiScreen + +object NestedScrollViewsScreen : UiScreen() { + + override val packageName: String = "com.kaspersky.kaspresso.kautomatorsample" + + val hscrollView = UiHorizontalScrollView { withId(this@NestedScrollViewsScreen.packageName, "hscrollView") } + val nnHscrollView = UiHorizontalScrollView { withId(this@NestedScrollViewsScreen.packageName, "nnHscrollView") } + val hbutton1 = UiButton { withId(this@NestedScrollViewsScreen.packageName, "hvText1") } + val hbutton7 = UiButton { withId(this@NestedScrollViewsScreen.packageName, "hvText7") } + val button1 = UiButton { withId(this@NestedScrollViewsScreen.packageName, "tvText1") } + val button20 = UiButton { withId(this@NestedScrollViewsScreen.packageName, "tvText20") } + val nnHbutton5 = UiButton { withId(this@NestedScrollViewsScreen.packageName, "nnHtvText5") } +} diff --git a/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/test/components/NestedScrollViewsTest.kt b/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/test/components/NestedScrollViewsTest.kt new file mode 100644 index 000000000..e7319f4f7 --- /dev/null +++ b/samples/kautomator-sample/src/androidTest/java/com/kaspersky/kaspresso/kautomatorsample/test/components/NestedScrollViewsTest.kt @@ -0,0 +1,85 @@ +package com.kaspersky.kaspresso.kautomatorsample.test.components + +import androidx.test.ext.junit.rules.activityScenarioRule +import com.kaspersky.kaspresso.kautomatorsample.scroll.NestedScrollViewsActivity +import com.kaspersky.kaspresso.kautomatorsample.screen.NestedScrollViewsScreen +import com.kaspersky.kaspresso.testcases.api.testcase.TestCase +import org.junit.Rule +import org.junit.Test + +/** + * This sample shows how to work with nested vertical and horizontal ScrollViews + */ +class NestedScrollViewsTest : TestCase() { + + @get:Rule + val activityRule = activityScenarioRule() + + @Test + fun click_last_button_in_ScrollView_and_then_first_one() = + run { + step("Click button20 in ScrollView, last item") { + NestedScrollViewsScreen { + button20.click() + } + } + + step("Click button1 in ScrollView, first item") { + NestedScrollViewsScreen { + button1.click() + } + } + } + + @Test + fun click_last_button_in_ScrollView_and_then_last_one_in_HorizontalScrollView() = + run { + step("Click button20 in ScrollView, last item") { + NestedScrollViewsScreen { + button20.click() + } + } + + step("Click hbutton7 in HorizontalScrollView, first item") { + NestedScrollViewsScreen { + hscrollView.scrollToView(hbutton7) + hbutton7.click() + } + } + } + + @Test + fun click_last_button_in_HorizontalScrollView_and_then_first_one() = + run { + step("Click hbutton7 in HorizontalScrollView, last item") { + NestedScrollViewsScreen { + hscrollView.scrollToView(hbutton7) + hbutton7.click() + } + } + step("Click hbutton1 in HorizontalScrollView, first item") { + NestedScrollViewsScreen { + hscrollView.scrollToView(hbutton1) + hbutton1.click() + } + } + } + + @Test + fun click_last_button_in_3LevelNestedHorizontalScrollView_and_then_last_one_in_1LevelNestedHorizontalScrollView() = + run { + step("Click nnHbutton5 in 3LevelNestedScrollView, middle item") { + NestedScrollViewsScreen { + nnHscrollView.scrollToView(nnHbutton5) + nnHbutton5.click() + } + } + + step("Click button7 in HorizontalScrollView, last item") { + NestedScrollViewsScreen { + hscrollView.scrollToView(hbutton7) + hbutton7.click() + } + } + } +} diff --git a/samples/kautomator-sample/src/main/AndroidManifest.xml b/samples/kautomator-sample/src/main/AndroidManifest.xml index 3cd3daded..22bd60fb1 100644 --- a/samples/kautomator-sample/src/main/AndroidManifest.xml +++ b/samples/kautomator-sample/src/main/AndroidManifest.xml @@ -26,6 +26,7 @@ + diff --git a/samples/kautomator-sample/src/main/java/com/kaspersky/kaspresso/kautomatorsample/scroll/NestedScrollViewsActivity.kt b/samples/kautomator-sample/src/main/java/com/kaspersky/kaspresso/kautomatorsample/scroll/NestedScrollViewsActivity.kt new file mode 100644 index 000000000..21698a63c --- /dev/null +++ b/samples/kautomator-sample/src/main/java/com/kaspersky/kaspresso/kautomatorsample/scroll/NestedScrollViewsActivity.kt @@ -0,0 +1,13 @@ +package com.kaspersky.kaspresso.kautomatorsample.scroll + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.kaspersky.kaspresso.kautomatorsample.R + +class NestedScrollViewsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_nested_scrollviews) + } +} diff --git a/samples/kautomator-sample/src/main/res/layout/activity_nested_scrollviews.xml b/samples/kautomator-sample/src/main/res/layout/activity_nested_scrollviews.xml new file mode 100644 index 000000000..9c3605cfc --- /dev/null +++ b/samples/kautomator-sample/src/main/res/layout/activity_nested_scrollviews.xml @@ -0,0 +1,387 @@ + + + + + + + + + +