Skip to content

Commit 38d0c6b

Browse files
Refactor; import resource
1 parent 3a96ccd commit 38d0c6b

File tree

4 files changed

+52
-69
lines changed

4 files changed

+52
-69
lines changed

app/src/androidTest/kotlin/com/itsaky/androidide/PermissionsInfoScreenTest.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.test.platform.app.InstrumentationRegistry
77
import androidx.test.uiautomator.UiSelector
88
import com.itsaky.androidide.activities.SplashActivity
99
import com.itsaky.androidide.helper.advancePastWelcomeScreen
10+
import com.itsaky.androidide.resources.R as ResourcesR
1011
import com.itsaky.androidide.screens.OnboardingScreen
1112
import com.itsaky.androidide.screens.PermissionsInfoScreen
1213
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
@@ -28,17 +29,17 @@ class PermissionsInfoScreenTest : TestCase() {
2829

2930
private val acceptText: String
3031
get() = targetContext.getString(
31-
com.itsaky.androidide.resources.R.string.privacy_disclosure_accept,
32+
ResourcesR.string.privacy_disclosure_accept,
3233
)
3334

3435
private val learnMoreText: String
3536
get() = targetContext.getString(
36-
com.itsaky.androidide.resources.R.string.privacy_disclosure_learn_more,
37+
ResourcesR.string.privacy_disclosure_learn_more,
3738
)
3839

3940
private val dialogTitle: String
4041
get() = targetContext.getString(
41-
com.itsaky.androidide.resources.R.string.privacy_disclosure_title,
42+
ResourcesR.string.privacy_disclosure_title,
4243
)
4344

4445
@After

app/src/androidTest/kotlin/com/itsaky/androidide/helper/DevicePermissionGrantUiHelper.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.itsaky.androidide.helper
22

33
import android.Manifest
4+
import android.view.accessibility.AccessibilityNodeInfo
45
import androidx.test.platform.app.InstrumentationRegistry
56
import com.kaspersky.kaspresso.device.Device
67

@@ -37,6 +38,44 @@ fun Device.grantDisplayOverOtherAppsUi() {
3738
grantViaAppOpsAndBack("SYSTEM_ALERT_WINDOW")
3839
}
3940

41+
/**
42+
* Finds accessibility nodes matching [searchText] and clicks the first one accepted by [matchBy].
43+
*
44+
* Handles root-window acquisition, node iteration, recycling, and throws
45+
* [AssertionError] if no matching node was clicked.
46+
*/
47+
fun clickFirstAccessibilityNodeByText(
48+
searchText: String,
49+
errorLabel: String = searchText,
50+
matchBy: (AccessibilityNodeInfo) -> Boolean = { node ->
51+
node.text?.toString().equals(searchText, ignoreCase = true) == true
52+
&& node.isClickable
53+
&& node.isEnabled
54+
&& node.isVisibleToUser
55+
},
56+
) {
57+
val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
58+
val root = uiAutomation.rootInActiveWindow
59+
?: throw AssertionError("No active window for accessibility")
60+
61+
val nodes = root.findAccessibilityNodeInfosByText(searchText)
62+
var clicked = false
63+
try {
64+
for (node in nodes) {
65+
if (!clicked && matchBy(node)) {
66+
clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
67+
}
68+
node.recycle()
69+
}
70+
} finally {
71+
root.recycle()
72+
}
73+
74+
if (!clicked) {
75+
throw AssertionError("No '$errorLabel' button found via accessibility")
76+
}
77+
}
78+
4079
private fun Device.grantViaAppOpsAndBack(appOp: String) {
4180
val pkg = InstrumentationRegistry.getInstrumentation().targetContext.packageName
4281
InstrumentationRegistry.getInstrumentation().uiAutomation

app/src/androidTest/kotlin/com/itsaky/androidide/helper/GrantRequiredPermissionsUiHelper.kt

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.itsaky.androidide.helper
22

33
import android.Manifest
4-
import android.view.accessibility.AccessibilityNodeInfo
54
import androidx.test.platform.app.InstrumentationRegistry
65
import com.itsaky.androidide.R
76
import com.itsaky.androidide.screens.PermissionScreen
@@ -63,32 +62,7 @@ fun TestContext<Unit>.grantAllRequiredPermissionsThroughOnboardingUi() {
6362
* "Allow" is always the next permission to grant.
6463
*/
6564
private fun clickFirstGrantButton() {
66-
val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
67-
val root = uiAutomation.rootInActiveWindow
68-
?: throw AssertionError("No active window for accessibility")
69-
7065
val ctx = InstrumentationRegistry.getInstrumentation().targetContext
7166
val grantText = ctx.getString(R.string.title_grant)
72-
73-
val nodes = root.findAccessibilityNodeInfosByText(grantText)
74-
var clicked = false
75-
try {
76-
for (node in nodes) {
77-
if (!clicked
78-
&& node.text?.toString().equals(grantText, ignoreCase = true)
79-
&& node.isClickable
80-
&& node.isEnabled
81-
&& node.isVisibleToUser
82-
) {
83-
clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
84-
}
85-
node.recycle()
86-
}
87-
} finally {
88-
root.recycle()
89-
}
90-
91-
if (!clicked) {
92-
throw AssertionError("No '$grantText' button found")
93-
}
67+
clickFirstAccessibilityNodeByText(grantText)
9468
}
Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.itsaky.androidide.helper
22

3-
import android.view.accessibility.AccessibilityNodeInfo
43
import androidx.test.platform.app.InstrumentationRegistry
54
import androidx.test.uiautomator.UiSelector
65
import com.kaspersky.kaspresso.testcases.core.testcontext.TestContext
@@ -17,26 +16,7 @@ fun TestContext<Unit>.passPermissionsInfoSlideWithPrivacyDialog() {
1716
val d = device.uiDevice
1817
val btn = d.findObject(UiSelector().text(accept))
1918
if (btn.waitForExists(2_000)) {
20-
// Use accessibility click -- button may be in the gesture exclusion zone
21-
val root = InstrumentationRegistry.getInstrumentation().uiAutomation
22-
.rootInActiveWindow
23-
if (root != null) {
24-
val nodes = root.findAccessibilityNodeInfosByText(accept)
25-
var clicked = false
26-
try {
27-
for (node in nodes) {
28-
if (!clicked && node.text?.toString().equals(accept, ignoreCase = true)) {
29-
clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
30-
}
31-
node.recycle()
32-
}
33-
} finally {
34-
root.recycle()
35-
}
36-
if (!clicked) {
37-
throw AssertionError("Failed to click '$accept' button via accessibility")
38-
}
39-
}
19+
clickFirstAccessibilityNodeByText(accept)
4020
d.waitForIdle()
4121
}
4222
}
@@ -50,26 +30,15 @@ fun TestContext<Unit>.passPermissionsInfoSlideWithPrivacyDialog() {
5030
throw AssertionError("NEXT button not found on permissions info slide")
5131
}
5232

53-
val uiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
54-
val root = uiAutomation.rootInActiveWindow
55-
?: throw AssertionError("No active window for accessibility")
56-
val nodes = root.findAccessibilityNodeInfosByText("NEXT")
57-
var clicked = false
58-
try {
59-
for (node in nodes) {
33+
clickFirstAccessibilityNodeByText(
34+
searchText = "NEXT",
35+
errorLabel = "NEXT",
36+
matchBy = { node ->
6037
val desc = node.contentDescription?.toString() ?: ""
6138
val text = node.text?.toString() ?: ""
62-
if (!clicked && (desc.contains("NEXT", ignoreCase = true) || text.contains("NEXT", ignoreCase = true))) {
63-
clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
64-
}
65-
node.recycle()
66-
}
67-
} finally {
68-
root.recycle()
69-
}
70-
if (!clicked) {
71-
throw AssertionError("NEXT button found by UIAutomator but accessibility click failed")
72-
}
39+
desc.contains("NEXT", ignoreCase = true) || text.contains("NEXT", ignoreCase = true)
40+
},
41+
)
7342
d.waitForIdle()
7443
}
7544
}

0 commit comments

Comments
 (0)