Skip to content

Commit 8b662ee

Browse files
committed
some ideas
1 parent 6b433fd commit 8b662ee

File tree

4 files changed

+168
-19
lines changed

4 files changed

+168
-19
lines changed

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/LandscapeIntegrationTest.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import org.catrobat.paintroid.R
4444
import org.catrobat.paintroid.colorpicker.HSVColorPickerView
4545
import org.catrobat.paintroid.colorpicker.PresetSelectorView
4646
import org.catrobat.paintroid.colorpicker.RgbSelectorView
47+
import org.catrobat.paintroid.test.espresso.util.EspressoUtils
4748
import org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackground
4849
import org.catrobat.paintroid.test.espresso.util.UiMatcher.withBackgroundColor
4950
import org.catrobat.paintroid.test.espresso.util.wrappers.BottomNavigationViewInteraction.Companion.onBottomNavigationView
@@ -57,7 +58,6 @@ import org.catrobat.paintroid.tools.ToolType
5758
import org.catrobat.paintroid.tools.options.ToolOptionsViewController
5859
import org.hamcrest.Matchers.allOf
5960
import org.hamcrest.Matchers.`is`
60-
import org.hamcrest.Matchers.not
6161
import org.junit.After
6262
import org.junit.Assert.assertEquals
6363
import org.junit.Before
@@ -109,14 +109,16 @@ class LandscapeIntegrationTest {
109109
if (tool) { continue }
110110
onToolBarView()
111111
.performSelectTool(toolType)
112-
if (toolOptionsViewController?.isVisible?.not() == true) {
112+
if (toolOptionsViewController?.isVisible == false) {
113113
onToolBarView()
114114
.performClickSelectedToolButton()
115115
}
116116
onBottomNavigationView()
117117
.onCurrentClicked()
118-
onView(withId(R.id.pocketpaint_layout_tool_specific_options))
119-
.check(matches(not(isDisplayed())))
118+
EspressoUtils.waitForViewToDisappear(withId(R.id.pocketpaint_layout_tool_specific_options))
119+
// BaseRobot().waitForViewToDisappear(withId(R.id.pocketpaint_layout_tool_specific_options)).check(matches(not(isDisplayed())))
120+
// onView(withId(R.id.pocketpaint_layout_tool_specific_options))
121+
// .check(matches(not(isDisplayed())))
120122
}
121123
}
122124

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/EspressoUtils.kt

Lines changed: 91 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,29 @@ import android.app.Activity
2424
import android.content.res.Configuration
2525
import android.graphics.PointF
2626
import android.os.Build
27+
import android.os.SystemClock
2728
import android.util.TypedValue
2829
import android.view.View
30+
import androidx.core.view.isVisible
2931
import androidx.test.espresso.Espresso
32+
import androidx.test.espresso.Espresso.onView
3033
import androidx.test.espresso.NoMatchingViewException
34+
import androidx.test.espresso.UiController
35+
import androidx.test.espresso.ViewAction
36+
import androidx.test.espresso.ViewAssertion
3137
import androidx.test.espresso.assertion.ViewAssertions
32-
import androidx.test.espresso.matcher.ViewMatchers
38+
import androidx.test.espresso.assertion.ViewAssertions.matches
3339
import androidx.test.platform.app.InstrumentationRegistry
3440
import androidx.test.rule.GrantPermissionRule
3541
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry
3642
import androidx.test.runner.lifecycle.Stage
3743
import org.catrobat.paintroid.MainActivity
3844
import org.hamcrest.Matcher
3945
import org.junit.Assert
46+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
47+
import androidx.test.espresso.matcher.ViewMatchers.isRoot
48+
import androidx.test.espresso.util.TreeIterables
49+
import org.hamcrest.Matchers.not
4050

4151
object EspressoUtils {
4252
const val DEFAULT_STROKE_WIDTH = 25
@@ -87,19 +97,55 @@ object EspressoUtils {
8797
val viewInteraction = Espresso.onView(viewMatcher).inRoot(UiMatcher.isToast)
8898
while (System.currentTimeMillis() < waitTime) {
8999
try {
90-
viewInteraction.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
100+
viewInteraction.check(ViewAssertions.matches(isDisplayed()))
91101
return
92102
} catch (e: NoMatchingViewException) {
93-
Espresso.onView(ViewMatchers.isRoot()).perform(UiInteractions.waitFor(250))
103+
Espresso.onView(isRoot()).perform(UiInteractions.waitFor(250))
104+
}
105+
}
106+
viewInteraction.check(ViewAssertions.matches(isDisplayed()))
107+
}
108+
109+
@SuppressWarnings("SwallowedException")
110+
fun waitForViewToDisappear(
111+
viewMatcher: org.hamcrest.Matcher<android.view.View>,
112+
timeoutMillis: Long = 10_000, // Default timeout 10 seconds
113+
pollIntervalMillis: Long = 100 // Default poll interval 100 ms
114+
) {
115+
val endTime = System.currentTimeMillis() + timeoutMillis
116+
var viewFound = true // Assume view is initially present
117+
118+
while (System.currentTimeMillis() < endTime && viewFound) {
119+
try {
120+
// Check if the view is still displayed
121+
onView(viewMatcher).check(matches(isDisplayed()))
122+
// If the check passes, the view is still displayed.
123+
// Wait for a short interval before trying again.
124+
onView(isRoot()).perform(UiInteractions.waitFor(pollIntervalMillis))
125+
} catch (e: NoMatchingViewException) {
126+
// View is not found in the hierarchy, so it has "disappeared" in this sense.
127+
viewFound = false
128+
} catch (e: AssertionError) {
129+
// View is in the hierarchy but not displayed (e.g., GONE, INVISIBLE).
130+
// This also means it has "disappeared" for the user.
131+
viewFound = false
132+
}
133+
}
134+
135+
if (viewFound) {
136+
try {
137+
onView(viewMatcher).check(matches(not(isDisplayed())))
138+
} catch (e: NoMatchingViewException) {
139+
// This is acceptable, means it's not in hierarchy
94140
}
95141
}
96-
viewInteraction.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
97142
}
98143

99144
val configuration: Configuration
100145
get() = InstrumentationRegistry.getInstrumentation().targetContext.resources.configuration
101146

102147
fun grantPermissionRulesVersionCheck(): GrantPermissionRule {
148+
103149
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
104150
GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE)
105151
} else {
@@ -109,4 +155,45 @@ object EspressoUtils {
109155
)
110156
}
111157
}
158+
fun searchFor(matcher: Matcher<View>): ViewAction {
159+
return object : ViewAction {
160+
161+
override fun getConstraints(): Matcher<View> {
162+
return isRoot()
163+
}
164+
165+
override fun getDescription(): String {
166+
return "searching for view $matcher in the root view"
167+
}
168+
169+
override fun perform(uiController: UiController, view: View) {
170+
val childViews: Iterable<View> = TreeIterables.breadthFirstViewTraversal(view)
171+
childViews.forEach {
172+
if (matcher.matches(it).and(it.isVisible)) {
173+
return
174+
}
175+
}
176+
throw NoMatchingViewException.Builder()
177+
.withRootView(view)
178+
.withViewMatcher(matcher)
179+
.build()
180+
}
181+
}
182+
}
183+
184+
@SuppressWarnings("SwallowedException")
185+
fun assertOnView(viewMatcher: Matcher<View?>?, assertion: ViewAssertion, waitMillis: Int = 10_000, waitMillisPerTry: Long = 50) {
186+
val endTime = System.currentTimeMillis() + waitMillis
187+
while (System.currentTimeMillis() < endTime) {
188+
try {
189+
onView(viewMatcher).check(assertion)
190+
return
191+
} catch (e: NoMatchingViewException) {
192+
SystemClock.sleep(waitMillisPerTry)
193+
} catch (e: AssertionError) {
194+
SystemClock.sleep(waitMillisPerTry)
195+
}
196+
}
197+
onView(viewMatcher).check(assertion)
198+
}
112199
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.catrobat.paintroid.test.espresso.util
2+
3+
import android.os.SystemClock.sleep
4+
import android.util.Log
5+
import android.view.View
6+
import androidx.test.espresso.Espresso.onView
7+
import androidx.test.espresso.ViewAction
8+
import androidx.test.espresso.ViewAssertion
9+
import androidx.test.espresso.ViewInteraction
10+
import androidx.test.espresso.matcher.ViewMatchers.isRoot
11+
import org.catrobat.paintroid.test.espresso.util.EspressoUtils.searchFor
12+
import org.hamcrest.Matcher
13+
14+
open class ViewRobot {
15+
16+
fun doOnView(matcher: Matcher<View>, vararg actions: ViewAction) {
17+
actions.forEach {
18+
waitForView(matcher).perform(it)
19+
}
20+
}
21+
22+
fun assertOnView(matcher: Matcher<View>, vararg assertions: ViewAssertion) {
23+
assertions.forEach {
24+
waitForView(matcher).check(it)
25+
}
26+
}
27+
28+
@SuppressWarnings("SwallowedException", "TooGenericExceptionThrown")
29+
private fun waitForView(viewMatcher: Matcher<View>, waitMillis: Int = 5000, waitMillisPerTry: Long = 50): ViewInteraction {
30+
val endTime = System.currentTimeMillis() + waitMillis
31+
while (System.currentTimeMillis() < endTime) {
32+
try {
33+
onView(isRoot()).perform(searchFor(viewMatcher))
34+
return onView(viewMatcher)
35+
} catch (e: Exception) {
36+
Log.d("asdf", "sleeping...")
37+
sleep(waitMillisPerTry)
38+
}
39+
}
40+
throw Exception("Error finding a view matching $viewMatcher after ${waitMillis}ms milliseconds")
41+
}
42+
43+
@SuppressWarnings("SwallowedException", "TooGenericExceptionThrown")
44+
fun waitForViewToDisappear(viewMatcher: Matcher<View>, waitMillis: Int = 5000, waitMillisPerTry: Long = 50): ViewInteraction {
45+
val endTime = System.currentTimeMillis() + waitMillis
46+
while (System.currentTimeMillis() < endTime) {
47+
try {
48+
onView(isRoot()).perform(searchFor(viewMatcher))
49+
sleep(waitMillisPerTry)
50+
} catch (e: Exception) {
51+
return onView(viewMatcher)
52+
}
53+
}
54+
onView(isRoot()).perform(searchFor(viewMatcher))
55+
throw Exception("View $viewMatcher did not disappear after ${waitMillis}ms")
56+
}
57+
}

Paintroid/src/androidTest/java/org/catrobat/paintroid/test/espresso/util/wrappers/BottomNavigationViewInteraction.kt

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,19 @@
1919
package org.catrobat.paintroid.test.espresso.util.wrappers
2020

2121
import androidx.test.espresso.Espresso
22+
import androidx.test.espresso.Espresso.onView
2223
import androidx.test.espresso.ViewInteraction
2324
import androidx.test.espresso.action.ViewActions
2425
import androidx.test.espresso.assertion.ViewAssertions
2526
import androidx.test.espresso.matcher.ViewMatchers
27+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
2628
import androidx.test.espresso.matcher.ViewMatchers.withId
29+
import androidx.test.espresso.matcher.ViewMatchers.withText
2730
import org.catrobat.paintroid.R
28-
import org.catrobat.paintroid.test.espresso.util.UiMatcher
2931
import org.catrobat.paintroid.tools.ToolType
3032
import org.hamcrest.Matchers.allOf
33+
import org.catrobat.paintroid.test.espresso.util.EspressoUtils
34+
import org.catrobat.paintroid.test.espresso.util.UiMatcher
3135

3236
class BottomNavigationViewInteraction private constructor() :
3337
CustomViewInteraction(Espresso.onView(withId(R.id.pocketpaint_bottom_navigation))) {
@@ -51,16 +55,15 @@ class BottomNavigationViewInteraction private constructor() :
5155
.perform(ViewActions.click())
5256
}
5357

54-
fun checkShowsCurrentTool(toolType: ToolType): ViewInteraction {
55-
Espresso.onView(
56-
allOf(
57-
withId(R.id.icon),
58-
ViewMatchers.isDescendantOfA(withId(R.id.action_current_tool))
59-
)
60-
)
61-
.check(ViewAssertions.matches(UiMatcher.withDrawable(toolType.drawableResource)))
62-
return Espresso.onView(withId(R.id.action_current_tool))
63-
.check(ViewAssertions.matches(ViewMatchers.hasDescendant(ViewMatchers.withText(toolType.nameResource))))
58+
fun checkShowsCurrentTool(toolType: ToolType) {
59+
var matcher = allOf(withId(R.id.icon),
60+
ViewMatchers.isDescendantOfA(withId(R.id.action_current_tool)),
61+
UiMatcher.withDrawable(R.drawable.ic_pocketpaint_tool_brush))
62+
var assertion = ViewAssertions.matches(isDisplayed())
63+
EspressoUtils.assertOnView(matcher, assertion)
64+
65+
matcher = allOf(withId(R.id.action_current_tool), ViewMatchers.hasDescendant(withText(toolType.nameResource)))
66+
EspressoUtils.assertOnView(matcher, assertion)
6467
}
6568

6669
fun onColorClicked(): ViewInteraction {

0 commit comments

Comments
 (0)