Skip to content

Commit

Permalink
(chocolateyGH-181) Add prompt_for_confirmation_short
Browse files Browse the repository at this point in the history
Allows the same behavior as prompt_for_confirmation, but in a short display. Example:

`Do you want to continue? (yes/no): `
  • Loading branch information
christianrondeau committed Mar 25, 2015
1 parent 31e3a62 commit 22af53b
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> 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<string>();
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<string> { "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<string> {"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<string> 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));
}
}
}
}
34 changes: 34 additions & 0 deletions src/chocolatey/infrastructure/commandline/InteractivePrompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,40 @@ private static IConsole Console
get { return _console.Value; }
}

public static string prompt_for_confirmation_short(string prompt, IEnumerable<string> 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))

This comment has been minimized.

Copy link
@ferventcoder

ferventcoder Mar 29, 2015

I'm gathering that .FirstOrDefault() returns the first letter of a string? Why not use something more explicit (and less confusing) like choice.Substring(0,1)? Not sure what the perf penalty is for either here.

Also use .to_string() (it might have the word safe in it) instead of .ToString() - it's null safe.

This comment has been minimized.

Copy link
@christianrondeau

christianrondeau Mar 29, 2015

Author Owner

FirstOrDefault has the advantage of allowing zero-length string. I'll do what you suggest, and add a validation at the beginning of the method to avoid making Substring crash.

{
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<string> choices, string defaultChoice, bool requireAnswer, int repeat = 10)
{
if (repeat < 0) throw new ApplicationException("Too many bad attempts. Stopping before application crash.");
Expand Down

0 comments on commit 22af53b

Please sign in to comment.