diff --git a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBody/Parsing/IssueFormBodyParser.cs b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBody/Parsing/IssueFormBodyParser.cs index 43a6820..2ef7adf 100644 --- a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBody/Parsing/IssueFormBodyParser.cs +++ b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBody/Parsing/IssueFormBodyParser.cs @@ -13,7 +13,10 @@ public static IssueFormBody Parse(IssueFormBodyText issueFormBodyText, IssueForm { var currentTemplateItem = templateItems[i]; var nextTemplateItem = templateItems.GetNextTemplateElement(i); - var (startIdx, valueLength) = GetLevel3HeaderValueIndexes(currentTemplateItem.Label, nextTemplateItem?.Label, issueFormBodyText); + var (startIdx, valueLength) = GetLevel3HeaderValueIndexes( + currentTemplateItem.Label, + nextTemplateItem?.Label, + issueFormBodyText); var bodyAsString = (string)issueFormBodyText; var value = bodyAsString.Substring(startIdx, valueLength); var issueFormItem = IssueFormItemFactory.CreateFormItem(currentTemplateItem.Id, currentTemplateItem.Type, value); @@ -50,9 +53,26 @@ private static (int startIdx, int valueLength) GetLevel3HeaderValueIndexes( private static int GetStartIndex(this IssueFormBodyText issueFormBodyText, string h3HeaderValue) { var bodyAsString = (string)issueFormBodyText; - var startIdx = bodyAsString.IndexOf(h3HeaderValue, StringComparison.Ordinal); - return startIdx is -1 - ? throw IssueFormBodyParserException.H3HeaderNotFound(h3HeaderValue) - : startIdx; + var h3HeaderValueWindowsLineEnding = h3HeaderValue + NewLines.CR + NewLines.LF; + var startIdx = bodyAsString.IndexOf(h3HeaderValueWindowsLineEnding, StringComparison.Ordinal); + if (startIdx is not -1) + { + return startIdx; + } + + var h3HeaderValueUnixLineEnding = h3HeaderValue + NewLines.LF; + startIdx = bodyAsString.IndexOf(h3HeaderValueUnixLineEnding, StringComparison.Ordinal); + if (startIdx is not -1) + { + return startIdx; + } + + throw IssueFormBodyParserException.H3HeaderNotFound(h3HeaderValue); + } + + internal static class NewLines + { + public const string CR = "\r"; + public const string LF = "\n"; } } diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/CliCommands/ParseIssueFormCommandTests.cs b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/CliCommands/ParseIssueFormCommandTests.cs index b10cf39..5d310dd 100644 --- a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/CliCommands/ParseIssueFormCommandTests.cs +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/CliCommands/ParseIssueFormCommandTests.cs @@ -44,7 +44,6 @@ public async Task ParseIssueFormCommandTest1() /// regardless of the line endings on the issue form body. /// [Theory] - [InlineData(CR)] [InlineData(LF)] [InlineData(CR + LF)] public async Task ParseIssueFormCommandTest2(string newLine) @@ -76,4 +75,27 @@ public async Task ParseIssueFormCommandTest2(string newLine) issueFormJson.OperatingSystems.Unknown.ShouldNotBeNull(); issueFormJson.OperatingSystems.Unknown.ShouldBe(false); } + + /// + /// Tests that the produces the expected JSON output. + /// This tests an edge case scenario for matching on the H3 header values. The matching is done + /// with an string.IndexOf method and if two H3 headers start with the same value then the matching + /// would always return the first occurence. + /// + /// This has been fixed by changing the matching so that it only matches if the H3 header value + /// matches for an entire line, not just the first string occurence. + /// + [Fact] + public async Task ParseIssueFormCommandTest3() + { + using var console = new FakeInMemoryConsole(); + var command = new ParseIssueFormCommand + { + IssueFormBody = File.ReadAllText("./TestFiles/IssueBody2.md").NormalizeLineEndings(), + TemplateFilepath = "./TestFiles/Template2.yml", + }; + await command.ExecuteAsync(console); + var output = console.ReadOutputString(); + output.ShouldNotBeNull(); + } } diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj index 3502b72..0b0c74e 100644 --- a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj @@ -48,6 +48,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -60,6 +63,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/IssueBody2.md b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/IssueBody2.md new file mode 100644 index 0000000..131d0b7 --- /dev/null +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/IssueBody2.md @@ -0,0 +1,7 @@ +### What NuGet package do you want to release? + +dotnet-sdk-extensions + +### What + +dotnet-sdk-extensions-testing diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/Template2.yml b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/Template2.yml new file mode 100644 index 0000000..da0fb3c --- /dev/null +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/TestFiles/Template2.yml @@ -0,0 +1,22 @@ +name: Release NuGet package +description: Release a NuGet package. +title: Release NuGet package +body: + - type: dropdown + id: nuget-id + attributes: + label: What NuGet package do you want to release? + options: + - dotnet-sdk-extensions + - dotnet-sdk-extensions-testing + validations: + required: true + - type: dropdown + id: nuget-id-2 + attributes: + label: What + options: + - dotnet-sdk-extensions + - dotnet-sdk-extensions-testing + validations: + required: true