Skip to content

Commit 6660f48

Browse files
Remove orchestrator
1 parent 38d0c6b commit 6660f48

10 files changed

Lines changed: 321 additions & 325 deletions

app/build.gradle.kts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ android {
101101
defaultConfig {
102102
applicationId = BuildConfig.PACKAGE_NAME
103103
vectorDrawables.useSupportLibrary = true
104+
testInstrumentationRunnerArguments["class"] = "com.itsaky.androidide.OrderedTestSuite"
104105
}
105106

106107
signingConfigs {
@@ -123,8 +124,6 @@ android {
123124
}
124125

125126
testOptions {
126-
execution = "ANDROIDX_TEST_ORCHESTRATOR"
127-
128127
unitTests {
129128
isIncludeAndroidResources = true
130129
all {
@@ -321,7 +320,7 @@ dependencies {
321320
androidTestImplementation(libs.tests.kaspresso)
322321
androidTestImplementation(libs.tests.junit.kts)
323322
androidTestImplementation(libs.tests.androidx.test.runner)
324-
androidTestUtil(libs.tests.orchestrator)
323+
// androidTestUtil(libs.tests.orchestrator)
325324
testImplementation(libs.tests.kotlinx.coroutines)
326325

327326
// brotli4j
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package com.itsaky.androidide
2+
3+
import android.view.accessibility.AccessibilityNodeInfo
4+
import androidx.test.core.app.ActivityScenario
5+
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
6+
import androidx.test.ext.junit.runners.AndroidJUnit4
7+
import androidx.test.platform.app.InstrumentationRegistry
8+
import androidx.test.uiautomator.UiSelector
9+
import com.itsaky.androidide.activities.SplashActivity
10+
import com.itsaky.androidide.helper.advancePastWelcomeScreen
11+
import com.itsaky.androidide.helper.clickFirstAccessibilityNodeByText
12+
import com.itsaky.androidide.helper.grantAllRequiredPermissionsThroughOnboardingUi
13+
import com.itsaky.androidide.resources.R as ResourcesR
14+
import com.itsaky.androidide.screens.OnboardingScreen
15+
import com.itsaky.androidide.screens.PermissionScreen
16+
import com.itsaky.androidide.screens.PermissionsInfoScreen
17+
import com.itsaky.androidide.utils.PermissionsHelper
18+
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
19+
import org.junit.Assert.assertEquals
20+
import org.junit.Assert.assertFalse
21+
import org.junit.Assert.assertTrue
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
25+
/**
26+
* Single continuous E2E test that drives the app from first launch through
27+
* onboarding, project creation, builds, and beyond.
28+
*
29+
* The activity launches once and stays alive. Each stage is a Kaspresso
30+
* `step()` so failures report exactly which stage broke.
31+
*/
32+
@RunWith(AndroidJUnit4::class)
33+
class EndToEndTest : TestCase() {
34+
35+
private val targetContext
36+
get() = InstrumentationRegistry.getInstrumentation().targetContext
37+
38+
private val acceptText: String
39+
get() = targetContext.getString(ResourcesR.string.privacy_disclosure_accept)
40+
41+
private val learnMoreText: String
42+
get() = targetContext.getString(ResourcesR.string.privacy_disclosure_learn_more)
43+
44+
private val dialogTitle: String
45+
get() = targetContext.getString(ResourcesR.string.privacy_disclosure_title)
46+
47+
private fun accessibilityClickByText(text: String): Boolean {
48+
val root = InstrumentationRegistry.getInstrumentation().uiAutomation
49+
.rootInActiveWindow ?: return false
50+
val nodes = root.findAccessibilityNodeInfosByText(text)
51+
var result = false
52+
try {
53+
for (node in nodes) {
54+
if (!result && node.text?.toString().equals(text, ignoreCase = true)) {
55+
result = node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
56+
}
57+
node.recycle()
58+
}
59+
} finally {
60+
root.recycle()
61+
}
62+
return result
63+
}
64+
65+
@Test
66+
fun test_endToEnd() = run {
67+
68+
// ── Launch ──
69+
70+
step("Launch app") {
71+
ActivityScenario.launch(SplashActivity::class.java)
72+
Thread.sleep(1000)
73+
}
74+
75+
// ── Welcome Screen ──
76+
77+
step("Verify welcome screen") {
78+
OnboardingScreen {
79+
greetingTitle.isVisible()
80+
greetingSubtitle.isVisible()
81+
nextButton {
82+
isVisible()
83+
isClickable()
84+
}
85+
}
86+
}
87+
88+
advancePastWelcomeScreen()
89+
90+
// ── Permissions Info Screen ──
91+
92+
step("Verify privacy disclosure dialog") {
93+
val d = device.uiDevice
94+
val title = d.findObject(UiSelector().text(dialogTitle))
95+
assertTrue("Dialog title missing", title.waitForExists(2_000))
96+
assertTrue("Accept button missing", d.findObject(UiSelector().text(acceptText)).exists())
97+
assertTrue("Learn more button missing", d.findObject(UiSelector().text(learnMoreText)).exists())
98+
}
99+
100+
step("Accept privacy disclosure") {
101+
assertTrue("Click failed", accessibilityClickByText(acceptText))
102+
device.uiDevice.waitForIdle()
103+
}
104+
105+
step("Verify permissions info content") {
106+
flakySafely(timeoutMs = 2_000) {
107+
PermissionsInfoScreen {
108+
introText { isVisible() }
109+
permissionsList { isVisible() }
110+
}
111+
}
112+
}
113+
114+
step("Verify NEXT button on permissions info") {
115+
OnboardingScreen.nextButton { isVisible(); isClickable() }
116+
}
117+
118+
step("Verify privacy dialog does not reappear") {
119+
assertFalse(
120+
"Dialog should not reappear",
121+
device.uiDevice.findObject(UiSelector().text(dialogTitle)).exists(),
122+
)
123+
}
124+
125+
// ── Permissions Screen ──
126+
127+
step("Advance to permissions list") {
128+
val d = device.uiDevice
129+
val nextObj = d.findObject(UiSelector().descriptionContains("NEXT"))
130+
if (!nextObj.waitForExists(3_000)) {
131+
throw AssertionError("NEXT button not found on permissions info slide")
132+
}
133+
clickFirstAccessibilityNodeByText(
134+
searchText = "NEXT",
135+
errorLabel = "NEXT",
136+
matchBy = { node ->
137+
val desc = node.contentDescription?.toString() ?: ""
138+
val text = node.text?.toString() ?: ""
139+
desc.contains("NEXT", ignoreCase = true) || text.contains("NEXT", ignoreCase = true)
140+
},
141+
)
142+
d.waitForIdle()
143+
}
144+
145+
val required = PermissionsHelper.getRequiredPermissions(targetContext)
146+
147+
step("Verify all permission items") {
148+
flakySafely(timeoutMs = 3_000) {
149+
PermissionScreen {
150+
title { isVisible() }
151+
subTitle { isVisible() }
152+
rvPermissions {
153+
isVisible()
154+
isDisplayed()
155+
}
156+
assertEquals(required.size, rvPermissions.getSize())
157+
158+
rvPermissions {
159+
required.forEachIndexed { index, item ->
160+
childAt<PermissionScreen.PermissionItem>(index) {
161+
title {
162+
isVisible()
163+
hasText(item.title)
164+
}
165+
description {
166+
isVisible()
167+
hasText(item.description)
168+
}
169+
grantButton {
170+
isVisible()
171+
isClickable()
172+
hasText(R.string.title_grant)
173+
}
174+
}
175+
}
176+
}
177+
}
178+
}
179+
}
180+
181+
grantAllRequiredPermissionsThroughOnboardingUi()
182+
183+
step("Confirm all permissions granted") {
184+
flakySafely(timeoutMs = 3_000) {
185+
assertTrue(PermissionsHelper.areAllPermissionsGranted(targetContext))
186+
}
187+
}
188+
189+
step("Confirm all grant buttons disabled") {
190+
device.uiDevice.waitForIdle()
191+
PermissionScreen {
192+
rvPermissions {
193+
required.indices.forEach { index ->
194+
childAt<PermissionScreen.PermissionItem>(index) {
195+
grantButton {
196+
isNotEnabled()
197+
}
198+
}
199+
}
200+
}
201+
}
202+
}
203+
204+
step("Tap Finish installation") {
205+
// The button is in the gesture exclusion zone — use accessibility click
206+
clickFirstAccessibilityNodeByText("Finish installation")
207+
}
208+
209+
step("Wait for IDE setup to complete") {
210+
com.itsaky.androidide.helper.waitForMainHomeOrEditorUi(
211+
device.uiDevice,
212+
maxWaitMs = 60_000L,
213+
)
214+
}
215+
216+
// ── Future phases (project creation, builds, preferences) go here ──
217+
}
218+
}

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

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import org.junit.runners.Suite
55

66
@RunWith(Suite::class)
77
@Suite.SuiteClasses(
8-
WelcomeScreenTest::class,
9-
PermissionsInfoScreenTest::class,
10-
PermissionsScreenTest::class,
11-
ProjectBuildTestWithKtsGradle::class,
128
CleanupTest::class,
9+
EndToEndTest::class,
1310
)
14-
class OrderedTestSuite
11+
class OrderedTestSuite

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

Lines changed: 0 additions & 133 deletions
This file was deleted.

0 commit comments

Comments
 (0)