diff --git a/src/chocolatey.tests/infrastructure/commandline/InteractivePromptSpecs.cs b/src/chocolatey.tests/infrastructure/commandline/InteractivePromptSpecs.cs index 0b3c4eaf66..10730a9543 100644 --- a/src/chocolatey.tests/infrastructure/commandline/InteractivePromptSpecs.cs +++ b/src/chocolatey.tests/infrastructure/commandline/InteractivePromptSpecs.cs @@ -437,5 +437,183 @@ public void should_error_when_any_choice_not_available_is_given() console.Verify(c => c.ReadLine(), Times.AtLeast(8)); } } + + public class when_prompting_short_with_interactivePrompt_guard_errors : InteractivePromptSpecsBase + { + private Func prompt; + + public override void Because() + { + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + prompt = () => InteractivePrompt.prompt_for_confirmation_short(prompt_value, choices); + } + + [Fact] + public void should_error_when_the_choicelist_is_null() + { + choices = null; + bool errored = false; + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + try + { + prompt(); + } + catch (Exception) + { + errored = true; + } + + errored.ShouldBeTrue(); + console.Verify(c => c.ReadLine(), Times.Never); + } + + [Fact] + public void should_error_when_the_choicelist_is_empty() + { + choices = new List(); + bool errored = false; + string errorMessage = string.Empty; + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + try + { + prompt(); + } + catch (Exception ex) + { + errored = true; + errorMessage = ex.Message; + } + + errored.ShouldBeTrue(); + errorMessage.ShouldContain("No choices passed in."); + console.Verify(c => c.ReadLine(), Times.Never); + } + + [Fact] + public void should_error_when_the_prompt_input_is_null() + { + choices = new List { "bob" }; + prompt_value = null; + bool errored = false; + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + try + { + prompt(); + } + catch (Exception) + { + errored = true; + } + + errored.ShouldBeTrue(); + console.Verify(c => c.ReadLine(), Times.Never); + } + + [Fact] + public void should_error_when_the_choicelist_has_multiple_items_with_same_first_letter() + { + choices = new List {"sally", "suzy"}; + bool errored = false; + string errorMessage = string.Empty; + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + try + { + prompt(); + } + catch (Exception ex) + { + errored = true; + errorMessage = ex.Message; + } + + errored.ShouldBeTrue(); + errorMessage.ShouldContain("Multiple choices have the same first letter. Please ensure you pass choices with different first letters."); + console.Verify(c => c.ReadLine(), Times.Never); + } + } + + public class when_prompting_short_with_interactivePrompt : InteractivePromptSpecsBase + { + private Func prompt; + + public override void Because() + { + prompt = () => InteractivePrompt.prompt_for_confirmation_short(prompt_value, choices); + } + + public override void AfterObservations() + { + base.AfterObservations(); + should_have_called_Console_ReadLine(); + } + + [Fact] + public void should_error_when_no_answer_given() + { + bool errored = false; + + console.Setup(c => c.ReadLine()).Returns(""); //Enter pressed + try + { + prompt(); + } + catch (Exception) + { + errored = true; + } + errored.ShouldBeTrue(); + console.Verify(c => c.ReadLine(), Times.AtLeast(8)); + } + + [Fact] + public void should_return_yes_when_yes_is_given() + { + console.Setup(c => c.ReadLine()).Returns("yes"); + var result = prompt(); + result.ShouldEqual("yes"); + } + + [Fact] + public void should_return_yes_when_y_is_given() + { + console.Setup(c => c.ReadLine()).Returns("y"); + var result = prompt(); + result.ShouldEqual("yes"); + } + + [Fact] + public void should_return_no_choice_when_no_is_given() + { + console.Setup(c => c.ReadLine()).Returns("no"); + var result = prompt(); + result.ShouldEqual("no"); + } + + [Fact] + public void should_return_no_choice_when_n_is_given() + { + console.Setup(c => c.ReadLine()).Returns("n"); + var result = prompt(); + result.ShouldEqual("no"); + } + + [Fact] + public void should_error_when_any_choice_not_available_is_given() + { + bool errored = false; + + console.Setup(c => c.ReadLine()).Returns("yup"); //Enter pressed + try + { + prompt(); + } + catch (Exception) + { + errored = true; + } + errored.ShouldBeTrue(); + console.Verify(c => c.ReadLine(), Times.AtLeast(8)); + } + } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs b/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs index 4f5eed890b..a8b2394944 100644 --- a/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs +++ b/src/chocolatey/infrastructure/commandline/InteractivePrompt.cs @@ -39,6 +39,40 @@ private static IConsole Console get { return _console.Value; } } + public static string prompt_for_confirmation_short(string prompt, IEnumerable choices, int repeat = 10) + { + if (repeat < 0) throw new ApplicationException("Too many bad attempts. Stopping before application crash."); + Ensure.that(() => prompt).is_not_null(); + Ensure.that(() => choices).is_not_null(); + Ensure + .that(() => choices) + .meets( + c => c.Any(), + (name, value) => { throw new ApplicationException("No choices passed in. Please ensure you pass choices."); }); + Ensure + .that(() => choices) + .meets( + c => c.Select(entry => entry.FirstOrDefault()).Distinct().Count() == c.Count(), + (name, value) => { throw new ApplicationException("Multiple choices have the same first letter. Please ensure you pass choices with different first letters."); }); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "{0} ({1}): ".format_with(prompt, String.Join("/", choices))); + + var selection = Console.ReadLine(); + + // check to see if value was passed + foreach (var choice in choices) + { + if (choice.is_equal_to(selection) || choice.FirstOrDefault().ToString().is_equal_to(selection)) + { + selection = choice; + return selection; + } + } + + "chocolatey".Log().Error(ChocolateyLoggers.Important, "Your choice of '{0}' is not a valid selection.".format_with(selection)); + return prompt_for_confirmation_short(prompt, choices, repeat - 1); + } + public static string prompt_for_confirmation(string prompt, IEnumerable choices, string defaultChoice, bool requireAnswer, int repeat = 10) { if (repeat < 0) throw new ApplicationException("Too many bad attempts. Stopping before application crash.");