Skip to content

Commit

Permalink
(chocolateyGH-181) Short prompt + (chocolateyGH-184) Prompt character
Browse files Browse the repository at this point in the history
  • Loading branch information
christianrondeau committed Jan 27, 2016
1 parent 7294952 commit a02cba1
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 8 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));
}
}
}
}
8 changes: 4 additions & 4 deletions src/chocolatey/infrastructure.app/runners/GenericRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ public void warn_when_admin_needs_elevation(ChocolateyConfiguration config)

if (shouldWarn)
{
var selection = InteractivePrompt.prompt_for_confirmation(@"
var selection = InteractivePrompt.prompt_for_confirmation_short(@"
You may experience errors - many functions/packages
require admin rights. Only advanced users should run choco w/out an
elevated shell. When you open the command shell, you should ensure
that you do so with ""Run as Administrator"" selected.
Do you want to continue?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true);
Do you want to continue?", new[] { "yes", "no" });

if (selection.is_equal_to("no"))
{
Expand All @@ -199,5 +199,5 @@ require admin rights. Only advanced users should run choco w/out an
}

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config)
var skipUninstaller = true;
if (config.PromptForConfirmation)
{
var selection = InteractivePrompt.prompt_for_confirmation("Uninstall may not be silent (could not detect). Proceed?", new[] {"yes", "no"}, defaultChoice: null, requireAnswer: true);
var selection = InteractivePrompt.prompt_for_confirmation_short("Uninstall may not be silent (could not detect). Proceed?", new[] {"yes", "no"});
if (selection.is_equal_to("yes")) skipUninstaller = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ private void rollback_previous_version(ChocolateyConfiguration config, PackageRe
var rollback = true;
if (config.PromptForConfirmation)
{
var selection = InteractivePrompt.prompt_for_confirmation(" Unsuccessful operation for {0}.{1} Do you want to rollback to previous version (package files only)?".format_with(packageResult.Name, Environment.NewLine), new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true);
var selection = InteractivePrompt.prompt_for_confirmation_short(" Unsuccessful operation for {0}.{1} Do you want to rollback to previous version (package files only)?".format_with(packageResult.Name, Environment.NewLine), new[] { "yes", "no" });
if (selection.is_equal_to("no")) rollback = false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,17 @@ 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"))
{
this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------");
this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces()));
this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------");
selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true);
selection = InteractivePrompt.prompt_for_confirmation_short(@"Do you want to run this script?", new[] { "yes", "no" });
}


if (selection.is_equal_to("yes")) shouldRun = true;
if (selection.is_equal_to("no"))
{
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 a02cba1

Please sign in to comment.