Skip to content

Commit

Permalink
VisibleCenterGlobalCoordinatesProvider (#84)
Browse files Browse the repository at this point in the history
* ClickAction now accepts CoordinatesProvider

add VisibleCenterGlobalCoordinatesProvider for special cases with translation animations involved

* add test to demonstrate transition animation click problem and VisibleCenterGlobalCoordinatesProvider usability

add tests that shows VisibleCenterGlobalCoordinatesProvider is also ok for general usage

* refactor RelativeCoordinatesProvider, use assertThrows

* trying to make AnimatedButtonClickTest fails more reproducible

* remove debug sleep

* ignore assertThrows tests in AnimatedButtonClickTest
  • Loading branch information
dsvoronin authored Jul 8, 2023
1 parent 4936d30 commit de519bb
Show file tree
Hide file tree
Showing 18 changed files with 223 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.github.kakaocup.kakao.ext.clicks

import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.Press
import io.github.kakaocup.kakao.common.actions.clicks.ClickAction
import io.github.kakaocup.kakao.ext.clicks.inprocess.DoubleClickEvent
import io.github.kakaocup.kakao.ext.clicks.inprocess.InProcessClickAction
import io.github.kakaocup.kakao.ext.clicks.visualization.VisualClicksConfig

class KakaoDoubleClick(private val visualClicksConfig: VisualClicksConfig? = null) : ClickAction {
override fun click(location: GeneralLocation) = InProcessClickAction(
override fun click(location: CoordinatesProvider) = InProcessClickAction(
clickEvent = DoubleClickEvent(visualClicksConfig),
coordinatesProvider = location,
precisionDescriber = Press.FINGER,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.github.kakaocup.kakao.ext.clicks

import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.Press
import io.github.kakaocup.kakao.common.actions.clicks.ClickAction
import io.github.kakaocup.kakao.ext.clicks.inprocess.InProcessClickAction
import io.github.kakaocup.kakao.ext.clicks.inprocess.LongClickEvent
import io.github.kakaocup.kakao.ext.clicks.visualization.VisualClicksConfig

class KakaoLongClick(private val visualClicksConfig: VisualClicksConfig? = null) : ClickAction {
override fun click(location: GeneralLocation) = InProcessClickAction(
override fun click(location: CoordinatesProvider) = InProcessClickAction(
clickEvent = LongClickEvent(visualClicksConfig),
coordinatesProvider = location,
precisionDescriber = Press.FINGER,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package io.github.kakaocup.kakao.ext.clicks

import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.Press
import io.github.kakaocup.kakao.common.actions.clicks.ClickAction
import io.github.kakaocup.kakao.ext.clicks.inprocess.SingleClickEvent
import io.github.kakaocup.kakao.ext.clicks.inprocess.InProcessClickAction
import io.github.kakaocup.kakao.ext.clicks.inprocess.SingleClickEvent
import io.github.kakaocup.kakao.ext.clicks.visualization.VisualClicksConfig

class KakaoSingleClick(private val visualClicksConfig: VisualClicksConfig? = null) : ClickAction {
override fun click(location: GeneralLocation) = InProcessClickAction(
override fun click(location: CoordinatesProvider) = InProcessClickAction(
clickEvent = SingleClickEvent(visualClicksConfig),
coordinatesProvider = location,
precisionDescriber = Press.FINGER,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.kakaocup.kakao.ext.clicks.coordinates

import android.view.View
import androidx.test.espresso.action.CoordinatesProvider

internal class RelativeCoordinatesProvider(
private val coordinatesProvider: CoordinatesProvider
) : CoordinatesProvider {
override fun calculateCoordinates(view: View): FloatArray {
val (offsetX, offsetY) = IntArray(2).apply(view.rootView::getLocationOnScreen)
val (viewX, viewY) = coordinatesProvider.calculateCoordinates(view)
return floatArrayOf(viewX - offsetX, viewY - offsetY)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.kakaocup.kakao.ext.clicks.coordinates

import android.graphics.Rect
import android.view.View
import androidx.test.espresso.action.CoordinatesProvider

/**
* Returns center of a visible part
*
* It's a replacement of androidx.test.espresso.action.GeneralLocation.VISIBLE_CENTER.
* Espresso implementation can miss on property animated views (e.g. rotated and scaled)
*
* Known issues:
* - It can miss in case of clipping when a view is rotated.
*/
class VisibleCenterGlobalCoordinatesProvider : CoordinatesProvider {
override fun calculateCoordinates(view: View): FloatArray {
val rect = Rect().apply(view::getGlobalVisibleRect)
return floatArrayOf(rect.centerX().toFloat(), rect.centerY().toFloat())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import android.webkit.WebView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.PrecisionDescriber
import io.github.kakaocup.kakao.ext.clicks.coordinates.RelativeCoordinatesProvider
import io.github.kakaocup.kakao.ext.clicks.visualization.VisualClicksConfig
import org.hamcrest.Description
import org.hamcrest.Matcher
Expand All @@ -32,15 +34,20 @@ class InProcessClickAction(
val rootView = view.rootView
val precision = precisionDescriber.describePrecision()

val (offsetX, offsetY) = IntArray(2).apply(rootView::getLocationOnScreen)
val (viewX, viewY) = coordinatesProvider.calculateCoordinates(view)
val coordinates = floatArrayOf(viewX - offsetX, viewY - offsetY)
// In the case of custom CoordinatesProvider we expect user to handle relativism
// and also opening the room for clicking on non-relative location, see [VisibleCenterGlobalCoordinatesProvider]
// [RelativeCoordinatesProvider] is still available in public API to wrap your custom location
val coordinatesProvider = if (coordinatesProvider is GeneralLocation) {
RelativeCoordinatesProvider(coordinatesProvider)
} else {
coordinatesProvider
}

clickEvent.perform(
uiController = uiController,
view = view,
rootView = rootView,
coordinates = coordinates,
coordinates = coordinatesProvider.calculateCoordinates(view),
precision = precision,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package io.github.kakaocup.kakao.common.actions
import android.view.View
import androidx.test.espresso.FailureHandler
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.ViewActions
import io.github.kakaocup.kakao.Kakao
Expand All @@ -30,7 +31,7 @@ interface BaseActions {
*
* @param location Location of view where it should be clicked (VISIBLE_CENTER by default)
*/
fun click(location: GeneralLocation = GeneralLocation.VISIBLE_CENTER) {
fun click(location: CoordinatesProvider = GeneralLocation.VISIBLE_CENTER) {
view.perform(Kakao.singleClickAction.click(location))
}

Expand All @@ -39,7 +40,7 @@ interface BaseActions {
*
* @param location Location of view where it should be clicked (VISIBLE_CENTER by default)
*/
fun doubleClick(location: GeneralLocation = GeneralLocation.VISIBLE_CENTER) {
fun doubleClick(location: CoordinatesProvider = GeneralLocation.VISIBLE_CENTER) {
view.perform(Kakao.doubleClickAction.click(location))
}

Expand All @@ -48,7 +49,7 @@ interface BaseActions {
*
* @param location Location of view where it should be clicked (VISIBLE_CENTER by default)
*/
fun longClick(location: GeneralLocation = GeneralLocation.VISIBLE_CENTER) {
fun longClick(location: CoordinatesProvider = GeneralLocation.VISIBLE_CENTER) {
view.perform(Kakao.longClickAction.click(location))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.kakaocup.kakao.common.actions.clicks

import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.CoordinatesProvider

interface ClickAction {
fun click(location: GeneralLocation): ViewAction
fun click(location: CoordinatesProvider): ViewAction
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package io.github.kakaocup.kakao.common.actions.clicks

import android.view.InputDevice
import android.view.MotionEvent
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralClickAction
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.Press
import androidx.test.espresso.action.Tap

class EspressoDoubleClick : ClickAction {
override fun click(location: GeneralLocation) = GeneralClickAction(
override fun click(location: CoordinatesProvider) = GeneralClickAction(
Tap.DOUBLE, location, Press.FINGER,
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package io.github.kakaocup.kakao.common.actions.clicks

import android.view.InputDevice
import android.view.MotionEvent
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralClickAction
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.Press
import androidx.test.espresso.action.Tap

class EspressoLongClick : ClickAction {
override fun click(location: GeneralLocation) = GeneralClickAction(
override fun click(location: CoordinatesProvider) = GeneralClickAction(
Tap.LONG, location, Press.FINGER,
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package io.github.kakaocup.kakao.common.actions.clicks
import android.view.InputDevice
import android.view.MotionEvent
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralClickAction
import androidx.test.espresso.action.GeneralLocation
import androidx.test.espresso.action.Press
import androidx.test.espresso.action.Tap

class EspressoSingleClick : ClickAction {
override fun click(location: GeneralLocation): ViewAction = GeneralClickAction(
override fun click(location: CoordinatesProvider): ViewAction = GeneralClickAction(
Tap.SINGLE, location, Press.FINGER,
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.github.kakaocup.sample

import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import io.github.kakaocup.kakao.ext.clicks.coordinates.VisibleCenterGlobalCoordinatesProvider
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.sample.screen.AnimatedButtonClickScreen
import io.github.kakaocup.sample.tools.applyEspressoClickExtension
import io.github.kakaocup.sample.tools.applyKakaoClickExtension
import org.junit.Assert.assertThrows
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4ClassRunner::class)
class AnimatedButtonClickTest {
@Rule
@JvmField
val rule = ActivityScenarioRule(AnimatedButtonClickActivity::class.java)

@Ignore("Not failing on CI emulators as expected, passing locally")
@Test
fun testEspressoClickAction() {
Screen.onScreen<AnimatedButtonClickScreen> {
applyEspressoClickExtension()

animatedView.click()
assertThrows(AssertionError::class.java) {
clickIndicator.isVisible()
}
}
}

@Ignore("Not failing on CI emulators as expected, passing locally")
@Test
fun testKakaoClickAction() {
Screen.onScreen<AnimatedButtonClickScreen> {
applyKakaoClickExtension()

animatedView.click()
assertThrows(AssertionError::class.java) {
clickIndicator.isVisible()
}

applyEspressoClickExtension()
}
}

@Test
fun testKakaoClickActionOnGlobalCenter() {
Screen.onScreen<AnimatedButtonClickScreen> {
applyKakaoClickExtension()

animatedView.click(VisibleCenterGlobalCoordinatesProvider())
clickIndicator.isVisible()

applyEspressoClickExtension()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@ package io.github.kakaocup.sample

import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import io.github.kakaocup.kakao.Kakao
import io.github.kakaocup.kakao.common.actions.clicks.EspressoDoubleClick
import io.github.kakaocup.kakao.common.actions.clicks.EspressoLongClick
import io.github.kakaocup.kakao.common.actions.clicks.EspressoSingleClick
import io.github.kakaocup.kakao.ext.clicks.KakaoDoubleClick
import io.github.kakaocup.kakao.ext.clicks.KakaoLongClick
import io.github.kakaocup.kakao.ext.clicks.KakaoSingleClick
import io.github.kakaocup.kakao.ext.clicks.coordinates.VisibleCenterGlobalCoordinatesProvider
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.sample.screen.ButtonClickScreen
import io.github.kakaocup.sample.tools.applyEspressoClickExtension
Expand Down Expand Up @@ -39,7 +33,7 @@ class ButtonClickTest {
@Test
fun testKakaoClickAction() {
Screen.onScreen<ButtonClickScreen> {
applyEspressoClickExtension()
applyKakaoClickExtension()

button {
click()
Expand All @@ -48,7 +42,23 @@ class ButtonClickTest {
hasText("Long Click")
}

applyEspressoClickExtension()
}
}

@Test
fun testKakaoClickActionOnGlobalCenter() {
Screen.onScreen<ButtonClickScreen> {
applyKakaoClickExtension()

button {
click(VisibleCenterGlobalCoordinatesProvider())
hasText("Single Click")
longClick(VisibleCenterGlobalCoordinatesProvider())
hasText("Long Click")
}

applyEspressoClickExtension()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package io.github.kakaocup.sample

import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import io.github.kakaocup.kakao.Kakao
import io.github.kakaocup.kakao.ext.clicks.KakaoDoubleClick
import io.github.kakaocup.kakao.ext.clicks.KakaoLongClick
import io.github.kakaocup.kakao.ext.clicks.KakaoSingleClick
import io.github.kakaocup.kakao.ext.clicks.coordinates.VisibleCenterGlobalCoordinatesProvider
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.sample.screen.ButtonClickScreen
import io.github.kakaocup.sample.tools.applyEspressoClickExtension
Expand Down Expand Up @@ -45,4 +42,18 @@ class ButtonDoubleClickTest {
applyEspressoClickExtension()
}
}

@Test
fun testKakaoClickActionOnGlobalCenter() {
Screen.onScreen<ButtonClickScreen> {
applyKakaoClickExtension()

button {
doubleClick(VisibleCenterGlobalCoordinatesProvider())
hasText("Double click registered")
}

applyEspressoClickExtension()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.kakaocup.sample.screen

import io.github.kakaocup.kakao.common.views.KView
import io.github.kakaocup.kakao.screen.Screen
import io.github.kakaocup.kakao.text.KTextView
import io.github.kakaocup.sample.R

class AnimatedButtonClickScreen : Screen<AnimatedButtonClickScreen>() {
val animatedView = KView { withId(R.id.animated_view) }
val clickIndicator = KTextView { withId(R.id.animated_view_click_indicator) }
}
10 changes: 7 additions & 3 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,19 @@

<activity
android:name="io.github.kakaocup.sample.ImageViewActivity"
android:label="Text activity"
android:label="Image View Activity"
android:theme="@style/MaterialAppTheme" />
<activity
android:name="io.github.kakaocup.sample.ButtonClickActivity"
android:label="Text activity"
android:label="Button Click Activity"
android:theme="@style/MaterialAppTheme" />
<activity
android:name="io.github.kakaocup.sample.ButtonDoubleClickActivity"
android:label="Text activity"
android:label="Button Double Click Activity"
android:theme="@style/MaterialAppTheme" />
<activity
android:name="io.github.kakaocup.sample.AnimatedButtonClickActivity"
android:label="Animated Button Clicks Activity"
android:theme="@style/MaterialAppTheme" />
</application>

Expand Down
Loading

0 comments on commit de519bb

Please sign in to comment.