-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Expand file tree
/
Copy pathInstrumentedTestUtil.kt
More file actions
143 lines (128 loc) · 4.8 KB
/
InstrumentedTestUtil.kt
File metadata and controls
143 lines (128 loc) · 4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package org.schabi.newpipe
import android.app.Instrumentation
import android.content.Context
import android.os.SystemClock
import android.view.MotionEvent
import androidx.annotation.StringRes
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
import androidx.compose.ui.test.TouchInjectionScope
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.hasScrollAction
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.swipeUp
import androidx.preference.PreferenceManager
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Assert.fail
/**
* Use this instead of calling `InstrumentationRegistry.getInstrumentation()` every time.
*/
val inst: Instrumentation
get() = InstrumentationRegistry.getInstrumentation()
/**
* Use this instead of passing contexts around in instrumented tests.
*/
val ctx: Context
get() = InstrumentationRegistry.getInstrumentation().targetContext
fun putBooleanInPrefs(@StringRes key: Int, value: Boolean) {
PreferenceManager.getDefaultSharedPreferences(ctx)
.edit().putBoolean(ctx.getString(key), value).apply()
}
fun putStringInPrefs(@StringRes key: Int, value: String) {
PreferenceManager.getDefaultSharedPreferences(ctx)
.edit().putString(ctx.getString(key), value).apply()
}
fun clearPrefs() {
PreferenceManager.getDefaultSharedPreferences(ctx)
.edit().clear().apply()
}
/**
* E.g. useful to tap outside dialogs to see whether they close.
*/
fun tapAtAbsoluteXY(x: Float, y: Float) {
val t = SystemClock.uptimeMillis()
inst.sendPointerSync(MotionEvent.obtain(t, t, MotionEvent.ACTION_DOWN, x, y, 0))
inst.sendPointerSync(MotionEvent.obtain(t, t + 50, MotionEvent.ACTION_UP, x, y, 0))
}
/**
* Same as the original `onNodeWithText` except that this takes a [StringRes] instead of a [String].
*/
fun SemanticsNodeInteractionsProvider.onNodeWithText(
@StringRes text: Int,
substring: Boolean = false,
ignoreCase: Boolean = false,
useUnmergedTree: Boolean = false
): SemanticsNodeInteraction {
return this.onNodeWithText(ctx.getString(text), substring, ignoreCase, useUnmergedTree)
}
/**
* Same as the original `onNodeWithContentDescription` except that this takes a [StringRes] instead of a [String].
*/
fun SemanticsNodeInteractionsProvider.onNodeWithContentDescription(
@StringRes text: Int,
substring: Boolean = false,
ignoreCase: Boolean = false,
useUnmergedTree: Boolean = false
): SemanticsNodeInteraction {
return this.onNodeWithContentDescription(ctx.getString(text), substring, ignoreCase, useUnmergedTree)
}
/**
* Shorthand for `.fetchSemanticsNode().positionOnScreen`.
*/
fun SemanticsNodeInteraction.fetchPosOnScreen() = fetchSemanticsNode().positionOnScreen
/**
* Asserts that [value] is in the range [[l], [r]] (both extremes included).
*/
fun <T : Comparable<T>> assertInRange(l: T, r: T, value: T) {
if (l > r) {
fail("Invalid range passed to `assertInRange`: [$l, $r]")
}
if (value !in l..r) {
fail("Expected $value to be in range [$l, $r]")
}
}
/**
* Asserts that [value] is NOT in the range [[l], [r]] (both extremes included).
*/
fun <T : Comparable<T>> assertNotInRange(l: T, r: T, value: T) {
if (l > r) {
fail("Invalid range passed to `assertInRange`: [$l, $r]")
}
if (value in l..r) {
fail("Expected $value to NOT be in range [$l, $r]")
}
}
/**
* Tries to scroll vertically in the container [this] and uses [itemInsideScrollingContainer] to
* compute how much the container actually scrolled. Useful in tandem with [assertMoved] or
* [assertDidNotMove].
*/
fun SemanticsNodeInteraction.scrollVerticallyAndGetOriginalAndFinalY(
itemInsideScrollingContainer: SemanticsNodeInteraction,
startY: TouchInjectionScope.() -> Float = { bottom },
endY: TouchInjectionScope.() -> Float = { top }
): Pair<Float, Float> {
val originalPosition = itemInsideScrollingContainer.fetchPosOnScreen()
this.performTouchInput { swipeUp(startY = startY(), endY = endY()) }
val finalPosition = itemInsideScrollingContainer.fetchPosOnScreen()
assertEquals(originalPosition.x, finalPosition.x)
return Pair(originalPosition.y, finalPosition.y)
}
/**
* Simple assert on results from [scrollVerticallyAndGetOriginalAndFinalY].
*/
fun Pair<Float, Float>.assertMoved() {
val (originalY, finalY) = this
assertNotEquals(originalY, finalY)
}
/**
* Simple assert on results from [scrollVerticallyAndGetOriginalAndFinalY].
*/
fun Pair<Float, Float>.assertDidNotMove() {
val (originalY, finalY) = this
assertEquals(originalY, finalY)
}