Fix Git editor reword workflow and UI coverage

- show Git editor file paths instead of the submitted git command
- surface follow-up commit message editors during interactive rebase reword
- add rename-commit UI tests for editor and amend-message solution paths
- add broader embedded level solution scenarios for alternate Git workflows
This commit is contained in:
Joe Tretter
2026-06-26 11:23:16 -05:00
parent 2db8fdd328
commit 781b5090f7
49 changed files with 372 additions and 19 deletions

View File

@@ -4,11 +4,16 @@ 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.performSemanticsAction
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performImeAction
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextClearance
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.text.AnnotatedString
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
@@ -44,12 +49,41 @@ class GitRepositoryRuntimeInstrumentedTest {
launchFreshApp().use {
allGithugLevels().forEachIndexed { index, level ->
waitForExercise(level)
level.testCases.first().commands.forEach(::submitTerminalCommand)
if (level.id == renameCommitLevel().id) {
completeRenameCommitWithEditor()
} else {
level.testCases.first().commands.forEach(::submitTerminalCommand)
}
waitForLevelCompletion(nextLevel = allGithugLevels().getOrNull(index + 1))
}
}
}
@Test
fun renameCommitLevelCompletesThroughUiEditor() {
launchFreshApp().use {
selectLevel(renameCommitLevel())
completeRenameCommitWithEditor()
waitForLevelCompletion(nextLevel = initLevel())
}
}
@Test
fun renameCommitLevelCompletesThroughMessageOptionAfterReword() {
launchFreshApp().use {
selectLevel(renameCommitLevel())
startRenameCommitReword()
composeRule.onNodeWithTag("git-message-editor-dismiss").performClick()
submitTerminalCommand("git commit --amend -m \"First commit\"")
submitTerminalCommand("git rebase --continue")
waitForLevelCompletion(nextLevel = initLevel())
}
}
@Test
fun configLevelCompletesThroughUiWithArbitraryValues() {
launchFreshApp().use {
@@ -111,6 +145,56 @@ class GitRepositoryRuntimeInstrumentedTest {
composeRule.onNodeWithTag("exercise-title-${nextLevel.id}").assertIsDisplayed()
}
private fun selectLevel(level: Level) {
composeRule.onNodeWithTag("level-${level.id}")
.performScrollTo()
.performClick()
waitForExercise(level)
composeRule.waitUntil(timeoutMillis = 30_000) {
composeRule.onAllNodes(hasText("Loaded level: ${level.title}", substring = true))
.fetchSemanticsNodes()
.isNotEmpty()
}
}
private fun completeRenameCommitWithEditor() {
startRenameCommitReword()
waitForGitEditorPath(".git/COMMIT_EDITMSG")
replaceGitEditorContent(
currentGitEditorContent().replaceFirst("First coommit", "First commit"),
)
composeRule.onNodeWithTag("git-message-editor-save").performClick()
}
private fun startRenameCommitReword() {
submitTerminalCommand("git rebase -i HEAD~2")
waitForGitEditorPath(".git/rebase-merge/git-rebase-todo")
replaceGitEditorContent(
currentGitEditorContent().replaceFirst(Regex("(?m)^pick "), "reword "),
)
composeRule.onNodeWithTag("git-message-editor-save").performClick()
}
private fun waitForGitEditorPath(path: String) {
composeRule.waitUntil(timeoutMillis = 30_000) {
composeRule.onAllNodes(hasText(path, substring = true)).fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithTag("git-message-editor-path").assertIsDisplayed()
}
private fun currentGitEditorContent(): String {
val node = composeRule.onNodeWithTag("git-message-editor-content").fetchSemanticsNode()
return node.config.getOrNull(SemanticsProperties.EditableText)?.text
?: error("Expected Git editor content semantics")
}
private fun replaceGitEditorContent(content: String) {
composeRule.onNodeWithTag("git-message-editor-content")
.performSemanticsAction(SemanticsActions.SetText) { setText ->
setText(AnnotatedString(content))
}
}
private fun submitTerminalCommand(command: String) {
val input = composeRule.onNodeWithTag("terminal-command-input")
input.performClick()