Skip to content

Commit

Permalink
(GH-181) Short prompt + (GH-184) Prompt character
Browse files Browse the repository at this point in the history
  • Loading branch information
christianrondeau committed Jul 22, 2015
1 parent c30b3a1 commit 891f8bb
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -437,5 +437,208 @@ 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;
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("Value for prompt cannot be null.");
console.Verify(c => c.ReadLine(), Times.Never);
}

[Fact]
public void should_error_when_the_choicelist_contains_empty_values()
{
choices = new List<string> { "bob", "" };
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("Some choices are empty.");
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.");
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));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack
this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider setting
'allowGlobalConfirmation'. Run 'choco feature -h' for more details.");

var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] {"yes", "no", "print"}, defaultChoice: null, requireAnswer: true);
var selection = InteractivePrompt.prompt_for_confirmation_short(@"Do you want to run the script?", new[] {"yes", "no", "print"});

if (selection.is_equal_to("print"))
{
Expand All @@ -231,6 +231,7 @@ public bool run_action(ChocolateyConfiguration configuration, PackageResult pack
selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true);
}


if (selection.is_equal_to("yes")) shouldRun = true;
if (selection.is_equal_to("no"))
{
Expand Down
5 changes: 5 additions & 0 deletions src/chocolatey/infrastructure/adapters/Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ namespace chocolatey.infrastructure.adapters

public sealed class Console : IConsole
{
public void Write(string value)
{
System.Console.Write(value);
}

public string ReadLine()
{
return System.Console.ReadLine();
Expand Down
28 changes: 4 additions & 24 deletions src/chocolatey/infrastructure/adapters/IConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,13 @@ namespace chocolatey.infrastructure.adapters

// ReSharper disable InconsistentNaming

/// <summary>
/// Adapter for <see cref="System.Console"/>
/// </summary>
public interface IConsole
{
/// <summary>
/// Reads the next line of characters from the standard input stream.
/// </summary>
/// <returns>
/// The next line of characters from the input stream, or null if no more lines are available.
/// </returns>
/// <exception cref="T:System.IO.IOException">
/// An I/O error occurred.
/// </exception>
/// <exception cref="T:System.OutOfMemoryException">
/// There is insufficient memory to allocate a buffer for the returned string.
/// </exception>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// The number of characters in the next line of characters is greater than <see cref="F:System.Int32.MaxValue" />.
/// </exception>
/// <filterpriority>1</filterpriority>
void Write(string value);
string ReadLine();

/// <summary>
/// Gets the standard error output stream.
/// </summary>
/// <returns>
/// A <see cref="T:System.IO.TextWriter" /> that represents the standard error output stream.
/// </returns>
/// <filterpriority>1</filterpriority>
TextWriter Error { get; }
}

Expand Down
43 changes: 43 additions & 0 deletions src/chocolatey/infrastructure/commandline/InteractivePrompt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,48 @@ 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.Any(String.IsNullOrWhiteSpace),
(name, value) => { throw new ApplicationException("Some choices are empty. Please ensure you provide no empty 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."); });

var promptWithChoices = "{0} ({1}): ".format_with(prompt, String.Join("/", choices));

Console.Write(promptWithChoices);
var selection = Console.ReadLine();

"chocolatey".Log().Info(ChocolateyLoggers.LogFileOnly, "{0}{1}".format_with(promptWithChoices, selection));

// check to see if value was passed
foreach (var choice in choices)
{
if (choice.is_equal_to(selection) || choice.Substring(0, 1).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<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 Expand Up @@ -69,6 +111,7 @@ public static string prompt_for_confirmation(string prompt, IEnumerable<string>
counter++;
}

Console.Write("> ");
var selection = Console.ReadLine();

if (string.IsNullOrWhiteSpace(selection) && defaultChoice != null)
Expand Down
2 changes: 2 additions & 0 deletions src/chocolatey/infrastructure/logging/ChocolateyLoggers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ public enum ChocolateyLoggers
Normal,
Verbose,
Important,
// Used to output prompt results in log file, but not in the console
LogFileOnly,
}
}
4 changes: 4 additions & 0 deletions src/chocolatey/infrastructure/logging/log4net.config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@
<appender-ref ref="VerboseLoggingColoredConsoleAppender" />
</logger>

<logger name="LogFileOnly">
<level value="INFO" />
</logger>

<logger name="chocolatey">
<level value="DEBUG"/>
<appender-ref ref="NormalLoggingColoredConsoleAppender" />
Expand Down

0 comments on commit 891f8bb

Please sign in to comment.