diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 467ae68..7725813 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,8 +20,8 @@ android { applicationId = "solutions.tretter.githugandroid" minSdk = 26 targetSdk = 35 - versionCode = 179 - versionName = "0.1.178" + versionCode = 180 + versionName = "0.1.179" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true diff --git a/app/src/androidTest/java/solutions/tretter/githugandroid/GitRepositoryRuntimeInstrumentedTest.kt b/app/src/androidTest/java/solutions/tretter/githugandroid/GitRepositoryRuntimeInstrumentedTest.kt index f4e1f72..d7696cd 100644 --- a/app/src/androidTest/java/solutions/tretter/githugandroid/GitRepositoryRuntimeInstrumentedTest.kt +++ b/app/src/androidTest/java/solutions/tretter/githugandroid/GitRepositoryRuntimeInstrumentedTest.kt @@ -3,6 +3,7 @@ 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.onAllNodesWithTag import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performSemanticsAction import androidx.compose.ui.test.performClick @@ -84,6 +85,25 @@ class GitRepositoryRuntimeInstrumentedTest { } } + @Test + fun squashLevelWaitsForFinalEditorBeforeCompleting() { + launchFreshApp().use { + selectLevel(squashLevel()) + + submitTerminalCommand("git rebase -i HEAD~4") + waitForGitEditorPath(".git/rebase-merge/git-rebase-todo") + replaceGitEditorContent(squashRebaseTodo(currentGitEditorContent())) + composeRule.onNodeWithTag("git-message-editor-save").performClick() + + waitForGitMessageEditor() + composeRule.onNodeWithTag("exercise-title-${squashLevel().id}").assertIsDisplayed() + + composeRule.onNodeWithTag("git-message-editor-save").performClick() + + waitForLevelCompletion(nextLevel = initLevel()) + } + } + @Test fun configLevelCompletesThroughUiWithArbitraryValues() { launchFreshApp().use { @@ -182,12 +202,28 @@ class GitRepositoryRuntimeInstrumentedTest { composeRule.onNodeWithTag("git-message-editor-path").assertIsDisplayed() } + private fun waitForGitMessageEditor() { + composeRule.waitUntil(timeoutMillis = 30_000) { + composeRule.onAllNodesWithTag("git-message-editor-content").fetchSemanticsNodes().isNotEmpty() + } + composeRule.onNodeWithTag("git-message-editor-content").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 squashRebaseTodo(content: String): String { + return content + .lineSequence() + .mapIndexed { index, line -> + if (index > 0 && line.startsWith("pick ")) line.replaceFirst("pick ", "squash ") else line + } + .joinToString("\n") + } + private fun replaceGitEditorContent(content: String) { composeRule.onNodeWithTag("git-message-editor-content") .performSemanticsAction(SemanticsActions.SetText) { setText -> diff --git a/app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt b/app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt index bd0159f..a7e7d39 100644 --- a/app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt +++ b/app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt @@ -388,11 +388,16 @@ fun GitHugApp() { fun applyCommandResult(raw: String, newRepo: RepoState, lines: List, echoCommand: Boolean) { val startedAt = System.nanoTime() val levelForResult = currentLevel - val solvedAfterCommand = levelForResult.validator(newRepo, raw) + val validationBlockedByEditor = editorState != null || gitMessageEditorState != null + val solvedAfterCommand = if (validationBlockedByEditor) { + false + } else { + levelForResult.validator(newRepo, raw) + } val wasAlreadyCompleted = currentLevel.id in completedLevels AppLog.d( "GitHugApp", - "Command='$raw' level=${levelForResult.id} solved=$solvedAfterCommand alreadyCompleted=$wasAlreadyCompleted " + + "Command='$raw' level=${levelForResult.id} solved=$solvedAfterCommand validationBlockedByEditor=$validationBlockedByEditor alreadyCompleted=$wasAlreadyCompleted " + "completedBefore=${completedLevels.sorted()} outputLineCount=${lines.size} repo=${newRepo.diagnosticSnapshot()}", ) val newOutput = buildList { @@ -519,14 +524,13 @@ fun GitHugApp() { invocation = state.invocation, message = state.content, ) - gitMessageEditorState = null - applyCommandResult(state.invocation.command, result.repo, result.outputLines, echoCommand = false) - result.nextEditor?.let { nextEditor -> - gitMessageEditorState = GitMessageEditorState( + gitMessageEditorState = result.nextEditor?.let { nextEditor -> + GitMessageEditorState( invocation = nextEditor.invocation, content = nextEditor.content, ) } + applyCommandResult(state.invocation.command, result.repo, result.outputLines, echoCommand = false) } fun runCommand() {