Defer level validation while editors are open
This commit is contained in:
@@ -20,8 +20,8 @@ android {
|
|||||||
applicationId = "solutions.tretter.githugandroid"
|
applicationId = "solutions.tretter.githugandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 179
|
versionCode = 180
|
||||||
versionName = "0.1.178"
|
versionName = "0.1.179"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package solutions.tretter.githugandroid
|
|||||||
import androidx.compose.ui.test.assertIsDisplayed
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
import androidx.compose.ui.test.hasText
|
import androidx.compose.ui.test.hasText
|
||||||
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
||||||
|
import androidx.compose.ui.test.onAllNodesWithTag
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.performSemanticsAction
|
import androidx.compose.ui.test.performSemanticsAction
|
||||||
import androidx.compose.ui.test.performClick
|
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
|
@Test
|
||||||
fun configLevelCompletesThroughUiWithArbitraryValues() {
|
fun configLevelCompletesThroughUiWithArbitraryValues() {
|
||||||
launchFreshApp().use {
|
launchFreshApp().use {
|
||||||
@@ -182,12 +202,28 @@ class GitRepositoryRuntimeInstrumentedTest {
|
|||||||
composeRule.onNodeWithTag("git-message-editor-path").assertIsDisplayed()
|
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 {
|
private fun currentGitEditorContent(): String {
|
||||||
val node = composeRule.onNodeWithTag("git-message-editor-content").fetchSemanticsNode()
|
val node = composeRule.onNodeWithTag("git-message-editor-content").fetchSemanticsNode()
|
||||||
return node.config.getOrNull(SemanticsProperties.EditableText)?.text
|
return node.config.getOrNull(SemanticsProperties.EditableText)?.text
|
||||||
?: error("Expected Git editor content semantics")
|
?: 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) {
|
private fun replaceGitEditorContent(content: String) {
|
||||||
composeRule.onNodeWithTag("git-message-editor-content")
|
composeRule.onNodeWithTag("git-message-editor-content")
|
||||||
.performSemanticsAction(SemanticsActions.SetText) { setText ->
|
.performSemanticsAction(SemanticsActions.SetText) { setText ->
|
||||||
|
|||||||
@@ -388,11 +388,16 @@ fun GitHugApp() {
|
|||||||
fun applyCommandResult(raw: String, newRepo: RepoState, lines: List<String>, echoCommand: Boolean) {
|
fun applyCommandResult(raw: String, newRepo: RepoState, lines: List<String>, echoCommand: Boolean) {
|
||||||
val startedAt = System.nanoTime()
|
val startedAt = System.nanoTime()
|
||||||
val levelForResult = currentLevel
|
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
|
val wasAlreadyCompleted = currentLevel.id in completedLevels
|
||||||
AppLog.d(
|
AppLog.d(
|
||||||
"GitHugApp",
|
"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()}",
|
"completedBefore=${completedLevels.sorted()} outputLineCount=${lines.size} repo=${newRepo.diagnosticSnapshot()}",
|
||||||
)
|
)
|
||||||
val newOutput = buildList {
|
val newOutput = buildList {
|
||||||
@@ -519,14 +524,13 @@ fun GitHugApp() {
|
|||||||
invocation = state.invocation,
|
invocation = state.invocation,
|
||||||
message = state.content,
|
message = state.content,
|
||||||
)
|
)
|
||||||
gitMessageEditorState = null
|
gitMessageEditorState = result.nextEditor?.let { nextEditor ->
|
||||||
applyCommandResult(state.invocation.command, result.repo, result.outputLines, echoCommand = false)
|
GitMessageEditorState(
|
||||||
result.nextEditor?.let { nextEditor ->
|
|
||||||
gitMessageEditorState = GitMessageEditorState(
|
|
||||||
invocation = nextEditor.invocation,
|
invocation = nextEditor.invocation,
|
||||||
content = nextEditor.content,
|
content = nextEditor.content,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
applyCommandResult(state.invocation.command, result.repo, result.outputLines, echoCommand = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runCommand() {
|
fun runCommand() {
|
||||||
|
|||||||
Reference in New Issue
Block a user