package solutions.tretter.githugandroid import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class InteractiveAddEngineTest { @Test fun interactiveStageShowsMenuWithoutStagingOrAutoCommands() { val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (updatedRepo, output) = GitSandboxEngine.execute(repo, "git stage -i") assertFalse(updatedRepo.files.single { it.name == "README" }.staged) assertTrue(output.any { it.contains("What now>") }) assertFalse(output.any { it.contains("What now> update") }) assertFalse(output.any { it.contains("What now> quit") }) assertFalse(output.any { it.contains("GitHug Android") }) } @Test fun interactiveAddShowsMenuWithoutStagingOrAutoCommands() { val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (updatedRepo, output) = GitSandboxEngine.execute(repo, "git add -i") assertFalse(updatedRepo.files.single { it.name == "README" }.staged) assertTrue(updatedRepo.interactiveAddSession != null) assertTrue(output.any { it.contains("What now>") }) assertFalse(output.any { it.contains("What now> update") }) assertFalse(output.any { it.contains("What now> quit") }) } @Test fun interactiveAddAcceptsUpdateSelectionFromNextInput() { val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (menuRepo, _) = GitSandboxEngine.execute(repo, "git stage -i") val (updateRepo, updateOutput) = GitSandboxEngine.execute(menuRepo, "2") val (selectedRepo, selectionOutput) = GitSandboxEngine.execute(updateRepo, "1") val (quitRepo, quitOutput) = GitSandboxEngine.execute(selectedRepo, "7") assertTrue(updateRepo.interactiveAddSession?.awaitingUpdateSelection == true) assertTrue(updateOutput.any { it.contains("Update>>") }) assertTrue(selectedRepo.files.single { it.name == "README" }.staged) assertTrue(selectedRepo.interactiveAddSession?.awaitingUpdateSelection == false) assertTrue(selectionOutput.any { it.contains("updated 1 path(s)") }) assertTrue(quitRepo.interactiveAddSession == null) assertTrue(quitOutput.any { it.contains("Bye.") }) } @Test fun interactiveAddHandlesEveryDisplayedMenuCommand() { val menuCommands = listOf( "1" to "What now> 1", "2" to "Update>>", "3" to "Revert>>", "4" to "Add untracked>>", "5" to "Patch update>>", "6" to "Diff>>", "7" to "Bye.", "8" to "What now> 8", ) menuCommands.forEach { (command, expectedOutput) -> val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (menuRepo, _) = GitSandboxEngine.execute(repo, "git add -i") val (updatedRepo, output) = GitSandboxEngine.execute(menuRepo, command) assertFalse("$command should not be rejected", output.any { it.contains("Huh ($command)?") }) assertTrue("$command should produce $expectedOutput", output.any { it.contains(expectedOutput) }) if (command in listOf("2", "3", "4", "5", "6")) { assertTrue(updatedRepo.interactiveAddSession?.awaitingUpdateSelection == true) } } } @Test fun interactiveAddHandlesMenuCommandAliases() { val aliases = listOf( "status" to "What now> status", "update" to "Update>>", "revert" to "Revert>>", "add untracked" to "Add untracked>>", "patch" to "Patch update>>", "diff" to "Diff>>", "quit" to "Bye.", "help" to "What now> help", ) aliases.forEach { (command, expectedOutput) -> val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (menuRepo, _) = GitSandboxEngine.execute(repo, "git add -i") val (updatedRepo, output) = GitSandboxEngine.execute(menuRepo, command) assertFalse("$command should not be rejected", output.any { it.contains("Huh ($command)?") }) assertTrue("$command should produce $expectedOutput", output.any { it.contains(expectedOutput) }) if (command in listOf("update", "revert", "add untracked", "patch", "diff")) { assertTrue(updatedRepo.interactiveAddSession?.awaitingUpdateSelection == true) } } } @Test fun interactiveAddPatchSelectionStagesSelectedPath() { val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (menuRepo, _) = GitSandboxEngine.execute(repo, "git add -i") val (patchRepo, patchOutput) = GitSandboxEngine.execute(menuRepo, "patch") val (hunkRepo, hunkOutput) = GitSandboxEngine.execute(patchRepo, "1") val (selectedRepo, selectionOutput) = GitSandboxEngine.execute(hunkRepo, "y") assertTrue(patchRepo.interactiveAddSession?.awaitingUpdateSelection == true) assertTrue(patchOutput.any { it.contains("Patch update>>") }) assertTrue(hunkOutput.any { it.contains("Stage this hunk") }) assertTrue(selectedRepo.files.single { it.name == "README" }.staged) assertTrue(selectionOutput.any { it.contains("Stage this hunk") && it.contains("y") }) } @Test fun patchAddStartsPatchHunkDialogWithoutStagingImmediately() { listOf("git add -p README", "git add --patch README").forEach { command -> val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (patchRepo, output) = GitSandboxEngine.execute(repo, command) assertFalse("$command should not stage before a selection", patchRepo.files.single { it.name == "README" }.staged) assertEquals("patch-hunk", patchRepo.interactiveAddSession?.selectionAction) assertTrue(output.any { it.startsWith("diff --git a/README b/README") }) assertTrue(output.any { it.contains("Stage this hunk") }) } } @Test fun patchAddSelectionStagesSelectedPath() { val repo = RepoState(initialized = true, files = listOf(GitFile("README"))) val (patchRepo, _) = GitSandboxEngine.execute(repo, "git add -p README") val (selectedRepo, output) = GitSandboxEngine.execute(patchRepo, "y") assertTrue(selectedRepo.files.single { it.name == "README" }.staged) assertTrue(output.any { it.contains("Stage this hunk") && it.contains("y") }) assertTrue(selectedRepo.interactiveAddSession == null) } @Test fun patchAddHunkEditOpensPatchEditorInvocation() { val repo = RepoState(initialized = true, files = listOf(GitFile("README", "A\nB\n", tracked = true))) val (patchRepo, _) = GitSandboxEngine.execute(repo, "git add -p README") val invocation = GitSandboxEngine.parsePatchHunkEditorInvocation(patchRepo, "e") assertEquals(GitEditorCommandKind.PATCH_HUNK, invocation?.kind) assertEquals("Edit Patch Hunk", invocation?.title) assertTrue(invocation?.initialContent.orEmpty().startsWith("# Manual hunk edit mode -- see bottom for a quick guide.")) assertFalse(invocation?.initialContent.orEmpty().contains("diff --git a/README b/README")) assertFalse(invocation?.initialContent.orEmpty().contains("--- a/README")) assertTrue(invocation?.initialContent.orEmpty().contains("# ---")) assertTrue(invocation?.initialContent.orEmpty().contains("# To remove '+' lines, delete them.")) assertTrue(invocation?.initialContent.orEmpty().contains("+A")) assertFalse(invocation?.initialContent.orEmpty().contains("Stage this hunk")) } @Test fun editedPatchHunkStagesCurrentPatchTarget() { val repo = RepoState(initialized = true, files = listOf(GitFile("README", "A\nB\n", tracked = true))) val (patchRepo, _) = GitSandboxEngine.execute(repo, "git add -p README") val invocation = GitSandboxEngine.parsePatchHunkEditorInvocation(patchRepo, "e") ?: error("Expected patch editor invocation") val (selectedRepo, output) = GitSandboxEngine.applyPatchHunkEdit(patchRepo, invocation.initialContent) assertTrue(selectedRepo.files.single { it.name == "README" }.staged) assertTrue(selectedRepo.interactiveAddSession == null) assertTrue(output.any { it.contains("Applied edited hunk.") }) } @Test fun patchAddHunkDialogHandlesAdvertisedCommands() { val commands = listOf("y", "n", "q", "a", "d", "s", "e", "p", "P", "?") commands.forEach { command -> val repo = RepoState(initialized = true, files = listOf(GitFile("README", "A\nB\n", tracked = true))) val (patchRepo, _) = GitSandboxEngine.execute(repo, "git add -p README") val (updatedRepo, output) = GitSandboxEngine.execute(patchRepo, command) assertFalse("$command should not be rejected", output.any { it.contains("Unknown command '$command'") }) assertTrue("$command should echo hunk prompt", output.any { it.contains("Stage this hunk") }) if (command == "e") { assertTrue(output.any { it.contains("Opening patch editor") }) } if (command in listOf("y", "a")) { assertTrue(updatedRepo.files.single { it.name == "README" }.staged) assertTrue(updatedRepo.interactiveAddSession == null) } } } }