11package org.schabi.newpipe
22
3+ import android.app.Instrumentation
34import android.content.Context
5+ import android.os.SystemClock
6+ import android.view.MotionEvent
47import androidx.annotation.StringRes
58import androidx.compose.ui.test.SemanticsNodeInteraction
69import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
10+ import androidx.compose.ui.test.TouchInjectionScope
11+ import androidx.compose.ui.test.assert
12+ import androidx.compose.ui.test.hasScrollAction
713import androidx.compose.ui.test.onNodeWithContentDescription
814import androidx.compose.ui.test.onNodeWithText
15+ import androidx.compose.ui.test.performTouchInput
16+ import androidx.compose.ui.test.swipeUp
917import androidx.preference.PreferenceManager
1018import androidx.test.platform.app.InstrumentationRegistry
19+ import org.junit.Assert.assertEquals
20+ import org.junit.Assert.assertNotEquals
1121import org.junit.Assert.fail
1222
23+ /* *
24+ * Use this instead of calling `InstrumentationRegistry.getInstrumentation()` every time.
25+ */
26+ val inst: Instrumentation
27+ get() = InstrumentationRegistry .getInstrumentation()
28+
1329/* *
1430 * Use this instead of passing contexts around in instrumented tests.
1531 */
@@ -31,6 +47,15 @@ fun clearPrefs() {
3147 .edit().clear().apply ()
3248}
3349
50+ /* *
51+ * E.g. useful to tap outside dialogs to see whether they close.
52+ */
53+ fun tapAtAbsoluteXY (x : Float , y : Float ) {
54+ val t = SystemClock .uptimeMillis()
55+ inst.sendPointerSync(MotionEvent .obtain(t, t, MotionEvent .ACTION_DOWN , x, y, 0 ))
56+ inst.sendPointerSync(MotionEvent .obtain(t, t + 50 , MotionEvent .ACTION_UP , x, y, 0 ))
57+ }
58+
3459/* *
3560 * Same as the original `onNodeWithText` except that this takes a [StringRes] instead of a [String].
3661 */
@@ -55,6 +80,11 @@ fun SemanticsNodeInteractionsProvider.onNodeWithContentDescription(
5580 return this .onNodeWithContentDescription(ctx.getString(text), substring, ignoreCase, useUnmergedTree)
5681}
5782
83+ /* *
84+ * Shorthand for `.fetchSemanticsNode().positionOnScreen`.
85+ */
86+ fun SemanticsNodeInteraction.fetchPosOnScreen () = fetchSemanticsNode().positionOnScreen
87+
5888/* *
5989 * Asserts that [value] is in the range [[l], [r]] (both extremes included).
6090 */
@@ -78,3 +108,36 @@ fun <T : Comparable<T>> assertNotInRange(l: T, r: T, value: T) {
78108 fail(" Expected $value to NOT be in range [$l , $r ]" )
79109 }
80110}
111+
112+ /* *
113+ * Tries to scroll vertically in the container [this] and uses [itemInsideScrollingContainer] to
114+ * compute how much the container actually scrolled. Useful in tandem with [assertMoved] or
115+ * [assertDidNotMove].
116+ */
117+ fun SemanticsNodeInteraction.scrollVerticallyAndGetOriginalAndFinalY (
118+ itemInsideScrollingContainer : SemanticsNodeInteraction ,
119+ startY : TouchInjectionScope .() -> Float = { bottom },
120+ endY : TouchInjectionScope .() -> Float = { top }
121+ ): Pair <Float , Float > {
122+ val originalPosition = itemInsideScrollingContainer.fetchPosOnScreen()
123+ this .performTouchInput { swipeUp(startY = startY(), endY = endY()) }
124+ val finalPosition = itemInsideScrollingContainer.fetchPosOnScreen()
125+ assertEquals(originalPosition.x, finalPosition.x)
126+ return Pair (originalPosition.y, finalPosition.y)
127+ }
128+
129+ /* *
130+ * Simple assert on results from [scrollVerticallyAndGetOriginalAndFinalY].
131+ */
132+ fun Pair <Float , Float >.assertMoved () {
133+ val (originalY, finalY) = this
134+ assertNotEquals(originalY, finalY)
135+ }
136+
137+ /* *
138+ * Simple assert on results from [scrollVerticallyAndGetOriginalAndFinalY].
139+ */
140+ fun Pair <Float , Float >.assertDidNotMove () {
141+ val (originalY, finalY) = this
142+ assertEquals(originalY, finalY)
143+ }
0 commit comments