Drive all levels through UI instrumentation

This commit is contained in:
Joe Tretter
2026-06-24 19:44:09 -05:00
parent 1c15297ae6
commit 1e87bd9aa7
8 changed files with 171 additions and 44 deletions

View File

@@ -1,13 +1,27 @@
package solutions.tretter.githugandroid
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performImeAction
import androidx.compose.ui.test.performTextClearance
import androidx.compose.ui.test.performTextInput
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class GitRepositoryRuntimeInstrumentedTest {
@get:Rule
val composeRule = createEmptyComposeRule()
@Test
fun nativeGitRuntimeCompletesInitLevelOnDevice() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
@@ -24,19 +38,50 @@ class GitRepositoryRuntimeInstrumentedTest {
}
@Test
fun nativeGitRuntimeCompletesConfigLevelOnDevice() {
fun allLevelsCompleteThroughAppTerminalUi() {
launchFreshApp().use {
allGithugLevels().forEachIndexed { index, level ->
waitForExercise(level)
level.testCases.first().commands.forEach(::submitTerminalCommand)
waitForLevelCompletion(nextLevel = allGithugLevels().getOrNull(index + 1))
}
}
}
private fun launchFreshApp(): ActivityScenario<MainActivity> {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val runtime = GitRepositoryRuntime(context)
val level = configLevel()
File(context.applicationInfo.dataDir, "files").deleteRecursively()
File(context.applicationInfo.dataDir, "cache").deleteRecursively()
val scenario = ActivityScenario.launch(MainActivity::class.java)
waitForExercise(initLevel())
return scenario
}
assertTrue(runtime.unavailableMessage(), runtime.isNativeGitAvailable())
private fun waitForExercise(level: Level) {
composeRule.waitUntil(timeoutMillis = 30_000) {
composeRule.onAllNodes(hasText(level.title, substring = true)).fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithTag("exercise-title-${level.id}").assertIsDisplayed()
}
val repo = runtime.prepareLevel(level)
val (namedRepo, _) = runtime.execute(level, repo, "git config user.name GitHug")
val (configuredRepo, _) = runtime.execute(level, namedRepo, "git config user.email githug@example.com")
private fun waitForLevelCompletion(nextLevel: Level?) {
if (nextLevel == null) {
composeRule.waitUntil(timeoutMillis = 30_000) {
composeRule.onAllNodes(hasText("All Githug levels completed", substring = true)).fetchSemanticsNodes().isNotEmpty()
}
return
}
composeRule.waitUntil(timeoutMillis = 30_000) {
composeRule.onAllNodes(hasText(nextLevel.title, substring = true)).fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithTag("exercise-title-${nextLevel.id}").assertIsDisplayed()
}
assertTrue(configuredRepo.config["user.name"].orEmpty().isNotBlank())
assertTrue(configuredRepo.config["user.email"].orEmpty().isNotBlank())
assertTrue(level.validator(configuredRepo, "git config user.email githug@example.com"))
private fun submitTerminalCommand(command: String) {
val input = composeRule.onNodeWithTag("terminal-command-input")
input.performClick()
input.performTextClearance()
input.performTextInput(command)
input.performImeAction()
}
}