Skip to content

Commit

Permalink
Add option input methods (#16)
Browse files Browse the repository at this point in the history
* Add option input methods

* Update README file to include option input methods

* Add unit tests for get option method

* Add unit tests for get yes/no option method
  • Loading branch information
Thomas-Shephard committed Feb 15, 2024
1 parent 3f5ca81 commit 22f6174
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
54 changes: 54 additions & 0 deletions IOUtils.Tests/InputTests/OptionTests/OptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using IOUtils.Input;
using NUnit.Framework;

namespace IOUtils.Tests.InputTests.OptionTests;

public class OptionTests {
private const string Question = "Example question text";

[TestCase("1", new[] {"Option 1", "Option 2"}, "Option 1")]
[TestCase("2", new[] {"Option 1", "Option 2"}, "Option 2")]
[TestCase("3", new[] {"Option 1", "Option 2", "Option 3"}, "Option 3")]
[TestCase("4", new[] {"Option 1", "Option 2", "Option 3", "Option 4"}, "Option 4")]
[TestCase("3", new[] {"Option 1", "Option 2", "Option 3", "Option 4", "Option 5"}, "Option 3")]
public void OptionInput_Options_ValidInput_ReturnsValue(string input, string[] options, string expected) {
MockProvider mockProvider = new(input);

string actual = OptionInput.GetOption(Question, options, mockProvider);

Assert.That(actual, Is.EqualTo(expected));
}

[TestCase("6", new[] {"Option 1", "Option 2"})]
[TestCase("0", new[] {"Option 1", "Option 2"})]
[TestCase("1.5", new[] {"Option 1", "Option 2"})]
[TestCase("1.0", new[] {"Option 1", "Option 2", "Option 3"})]
[TestCase("0", new[] {"Option 1", "Option 2", "Option 3", "Option 4"})]
[TestCase("-1", new[] {"Option 1", "Option 2", "Option 3"})]
[TestCase("abc", new[] {"Option 1", "Option 2"})]
[TestCase("Yes", new[] {"Option 1", "Option 2", "Option 3"})]
[TestCase("No", new[] {"Option 1", "Option 2"})]
[TestCase("", new[] {"Option 1", "Option 2", "Option 3", "Option 4"})]
public void OptionInput_Options_InvalidInput_ErrorOutput(string input, string[] options) {
MockProvider mockProvider = new(input);

Assert.Throws<InvalidOperationException>(() => OptionInput.GetOption(Question, options, mockProvider));

string expectedQuestion = Question;

for (int i = 0; i < options.Length; i++) {
expectedQuestion += $"{Environment.NewLine}{i + 1}. {options[i]}";
}

string expectedErrorMessage = $"That was not valid, enter a whole number between 1 and {options.Length}";

string[] expected = new[] { expectedQuestion, expectedErrorMessage };

Assert.That(mockProvider.OutputLines, Is.EquivalentTo(expected));
}

[Test]
public void OptionInput_Options_EmptyOptions_ThrowsException() {
Assert.Throws<ArgumentException>(() => OptionInput.GetOption(Question, Array.Empty<string>()));
}
}
45 changes: 45 additions & 0 deletions IOUtils.Tests/InputTests/OptionTests/YesNoOptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using IOUtils.Input;
using NUnit.Framework;

namespace IOUtils.Tests.InputTests.OptionTests;

public class YesNoOptionTests {
private const string Question = "Example question text";

[TestCase("1", true)]
[TestCase("2", false)]
public void OptionInput_YesNo_ValidInput_ReturnsValue(string input, bool expected) {
MockProvider mockProvider = new(input);

bool actual = OptionInput.GetYesNoOption(Question, provider: mockProvider);

Assert.That(actual, Is.EqualTo(expected));
}

[TestCase("3")]
[TestCase("1.5")]
[TestCase("1.0")]
[TestCase("0")]
[TestCase("-1")]
[TestCase("abc")]
[TestCase("Yes")]
[TestCase("No")]
[TestCase("")]
[TestCase(null)]
public void OptionInput_YesNo_InvalidInput_ErrorOutput(string input) {
MockProvider mockProvider = new(input);

Assert.Throws<InvalidOperationException>(() => OptionInput.GetYesNoOption(Question, provider: mockProvider));

string[] expected = {
$"""
{Question}
1. Yes
2. No
""",
"That was not valid, enter a whole number between 1 and 2"
};

Assert.That(mockProvider.OutputLines, Is.EquivalentTo(expected));
}
}
30 changes: 30 additions & 0 deletions IOUtils/Input/OptionInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System.Text;
using IOUtils.Providers;

namespace IOUtils.Input;

public static class OptionInput {
public static string GetOption(string question, string[] options, IProvider? provider = null) {
int responseIndex = GetOptionIndex(question, options, provider);

return options[responseIndex];
}

public static bool GetYesNoOption(string question, IProvider? provider = null) {
// The index of the "Yes" option is 0
return GetOptionIndex(question, new[] { "Yes", "No" }, provider) == 0;
}

public static int GetOptionIndex(string question, string[] options, IProvider? provider = null) {
if (options.Length < 1)
throw new ArgumentException("There must be at least one option to choose from", nameof(options));

StringBuilder questionStringBuilder = new(question);

for (int i = 0; i < options.Length; i++)
questionStringBuilder.Append($"{Environment.NewLine}{i + 1}. {options[i]}");

// The index of the selected option is 1 less than the numerical input
return NumericalInput<int>.Get(questionStringBuilder.ToString(), 1, options.Length, provider) - 1;
}
}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# IOUtils: Input and Output Utility Package for .NET

## Installation

Install the IOUtils NuGet package using the .NET CLI:

```
Expand All @@ -10,7 +11,10 @@ dotnet add package IOUtils
## Features

### Retrieve numerical input from the user
NumericalInput is a generic class that allows for the retrieval of a numerical input from the user. Optionally, a minimum and maximum value can be specified to restrict the input range.

NumericalInput is a generic class that allows for the retrieval of a numerical input from the user. Optionally, a
minimum and maximum value can be specified to restrict the input range.

```csharp
using IOUtils.Input;

Expand All @@ -20,8 +24,22 @@ float value = NumericalInput<float>.Get("Enter a negative number", max: 0);
decimal value = NumericalInput<decimal>.Get("Enter a number between 0 and 100", min: 0, max: 100);
```

### Retrieve option from the user

OptionInput is a class that allows for the retrieval of an option from the user.

```csharp
using IOUtils.Input;

string selectedOption = OptionInput.GetOption("Select an option", new[] { "Option 1", "Option 2", "Option 3" });
int optionIndex = OptionInput.GetOptionIndex("Select an option", new[] { "Option 1", "Option 2", "Option 3" });
bool doSomething = OptionInput.GetYesNoOption("Do you want to do something?");
```

## Contributions

Contributions are welcome! Read the [CONTRIBUTING](CONTRIBUTING.md) guide for information.

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE) for details.

0 comments on commit 22f6174

Please sign in to comment.