diff --git a/GherkinSimpleParser.Converter/CSVConverter.cs b/GherkinSimpleParser.Converter/CSVConverter.cs index 6e9f3c0..3ebc841 100644 --- a/GherkinSimpleParser.Converter/CSVConverter.cs +++ b/GherkinSimpleParser.Converter/CSVConverter.cs @@ -17,7 +17,7 @@ public List ExportAsCSV(string separator, GherkinObject gherkinObj) if (gherkinObj.Background.Givens.Count > 0) { - string givenBackground = string.Join(separator, gherkinObj.Background.Givens.Prepend("GENERAL PREREQUISITES:")); + string givenBackground = $"GENERAL PREREQUISITES:{separator}{BuildInstructionBatchLine(separator, gherkinObj.Background.Givens)}"; csv.Add($";{givenBackground};;"); } @@ -25,23 +25,45 @@ public List ExportAsCSV(string separator, GherkinObject gherkinObj) foreach (var scenario in gherkinObj.Scenarios) { csv.Add($"{testCount};{scenario.Name};;"); - csv.Add($";{string.Join(separator, scenario.Givens)};{scenario.When};{string.Join(separator, scenario.Thens)}"); + string givenCol = BuildInstructionBatchLine(separator, scenario.Givens); + string whenCol = scenario.When; + string thenCol = BuildInstructionBatchLine(separator, scenario.Thens); + csv.Add($";{givenCol};{whenCol};{thenCol}"); testCount++; } return csv; } + private string BuildInstructionBatchLine(string separator, List instructions) + { + var flattened = new List(); + foreach(var instruction in instructions) + { + var sb = new StringBuilder(); + sb.Append(instruction.MainLine); + if(instruction.DocStrings.Count > 0) + { + sb.Append(" \"").Append(string.Join(' ', instruction.DocStrings)).Append('"'); + } + flattened.Add(sb.ToString()); + } + return string.Join(separator, flattened); + } + + [Obsolete("Deprecated: Will be removed in future versions. Use/modify the ExcelConverter to your needs instead.")] public List ExportAsCSVWithExcelFormulaWrap_FR(GherkinObject gherkinObj) { return ExportAsCSVWithExcelFormulaWrap("\" & CAR(10) & \"", gherkinObj); } + [Obsolete("Deprecated: Will be removed in future versions. Use/modify the ExcelConverter to your needs instead.")] public List ExportAsCSVWithExcelFormulaWrap_EN(GherkinObject gherkinObj) { return ExportAsCSVWithExcelFormulaWrap("\" & CHAR(10) & \"", gherkinObj); } + [Obsolete("Deprecated: Will be removed in future versions. Use/modify the ExcelConverter to your needs instead.")] private List ExportAsCSVWithExcelFormulaWrap(string separator, GherkinObject gherkinObj) { var csv = new List @@ -51,7 +73,10 @@ private List ExportAsCSVWithExcelFormulaWrap(string separator, GherkinOb if (gherkinObj.Background.Givens.Count > 0) { - string givenBackground = string.Join(separator, gherkinObj.Background.Givens.Prepend("GENERAL PREREQUISITES:").Select(g => g.Replace("\"", "\"\""))); + string givenBackground = string.Join(separator, + gherkinObj.Background.Givens + .Prepend(new Instruction("GENERAL PREREQUISITES:")) + .Select(g => g.MainLine.Replace("\"", "\"\""))); csv.Add($";=\"{givenBackground}\";;"); } @@ -59,7 +84,10 @@ private List ExportAsCSVWithExcelFormulaWrap(string separator, GherkinOb foreach (var scenario in gherkinObj.Scenarios) { csv.Add($"{testCount};{scenario.Name};;"); - csv.Add($";=\"{string.Join(separator, scenario.Givens.Select(g => g.Replace("\"", "\"\"")))}\";{scenario.When};=\"{string.Join(separator, scenario.Thens.Select(g => g.Replace("\"", "\"\"")))}\""); + string givenCol = string.Join(separator, scenario.Givens.Select(g => g.MainLine.Replace("\"", "\"\""))); + string whenCol = scenario.When; + string thenCol = string.Join(separator, scenario.Thens.Select(g => g.MainLine.Replace("\"", "\"\""))); + csv.Add($";=\"{givenCol}\";{whenCol};=\"{thenCol}\""); testCount++; } diff --git a/GherkinSimpleParser.Converter/ExcelConverter.cs b/GherkinSimpleParser.Converter/ExcelConverter.cs index d66f758..c34b727 100644 --- a/GherkinSimpleParser.Converter/ExcelConverter.cs +++ b/GherkinSimpleParser.Converter/ExcelConverter.cs @@ -41,26 +41,18 @@ private void AppendSheetsToExcelFile(List featuresAndScenarios, E } } - private void SetColor(ExcelWorksheet ws, string range, Color color) - { - ws.Cells[range].Style.Fill.PatternType = ExcelFillStyle.Solid; - ws.Cells[range].Style.Fill.BackgroundColor.SetColor(color); - } - private void InsertSheetHeader(GherkinObject featureAndScenarios, ExcelWorksheet ws) { ws.Cells["B2"].Value = featureAndScenarios.FeatureName; ws.Cells["B2:E2"].Merge = true; ws.Cells["B2:E2"].Style.Font.Bold = true; ws.Cells["B2:E2"].Style.Font.Size = 20; - ws.Cells["B2:E2"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; - ws.Cells["B2:E2"].Style.VerticalAlignment = ExcelVerticalAlignment.Center; + ws.AlignCenter("B2:E2", true, true); ws.Cells["A4:F4"].Style.Font.Bold = true; ws.Cells["A4:F4"].Style.Font.Size = 12; - ws.Cells["A4:F4"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; - ws.Cells["A4:F4"].Style.VerticalAlignment = ExcelVerticalAlignment.Center; - SetColor(ws, "A4:F4", Color.FromArgb(255, 68, 114, 196)); + ws.AlignCenter("A4:F4", true, true); + ws.SetColor("A4:F4", Color.FromArgb(255, 68, 114, 196)); ws.Cells["A4"].Value = "Number"; ws.Cells["B4"].Value = "GIVEN"; ws.Cells["C4"].Value = "WHEN"; @@ -76,39 +68,57 @@ private void InsertGherkinObjectInSheet(GherkinObject featureAndScenarios, Excel { var generalPrerequisite = new StringBuilder(); generalPrerequisite.AppendLine("GENERAL PREREQUISITES:"); - foreach (var prerequisite in featureAndScenarios.Background.Givens) - { - generalPrerequisite.Append("- "); - generalPrerequisite.AppendLine(prerequisite); - } + generalPrerequisite.AppendLine(BuildInstructionBatch(featureAndScenarios.Background.Givens)); ws.Cells[$"B{lineToFillId}"].Value = generalPrerequisite.ToString(); - ws.Cells[$"B{lineToFillId}"].Style.VerticalAlignment = ExcelVerticalAlignment.Center; - SetColor(ws, $"A{lineToFillId}:F{lineToFillId}", Color.FromArgb(255, 180, 198, 231)); + ws.AlignCenter($"B{lineToFillId}", true, false); + ws.SetColor($"A{lineToFillId}:F{lineToFillId}", Color.FromArgb(255, 180, 198, 231)); lineToFillId++; } int testId = 1; foreach (var scenario in featureAndScenarios.Scenarios) { - ws.Cells[$"A{lineToFillId}"].Value = testId.ToString(); - ws.Cells[$"A{lineToFillId}:F{lineToFillId}"].Style.VerticalAlignment = ExcelVerticalAlignment.Center; - ws.Cells[$"A{lineToFillId}"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; + ws.Cells[$"A{lineToFillId}"].Value = testId; + ws.AlignCenter($"A{lineToFillId}:F{lineToFillId}", true, false); + ws.AlignCenter($"A{lineToFillId}", false, true); ws.Cells[$"B{lineToFillId}"].Value = scenario.Name; - SetColor(ws, $"A{lineToFillId}:F{lineToFillId}", Color.FromArgb(255, 180, 198, 231)); + ws.SetColor($"A{lineToFillId}:F{lineToFillId}", Color.FromArgb(255, 180, 198, 231)); lineToFillId++; - string givens = string.Join('\n', scenario.Givens.Select(x => "- " + x)); - ws.Cells[$"B{lineToFillId}"].Value = givens; + ws.Cells[$"B{lineToFillId}"].Value = BuildInstructionBatch(scenario.Givens); ws.Cells[$"C{lineToFillId}"].Value = scenario.When; - string thens = string.Join('\n', scenario.Thens.Select(x => "- " + x)); - ws.Cells[$"D{lineToFillId}"].Value = thens; - ws.Cells[$"A{lineToFillId}:F{lineToFillId}"].Style.VerticalAlignment = ExcelVerticalAlignment.Center; + ws.Cells[$"D{lineToFillId}"].Value = BuildInstructionBatch(scenario.Thens); + ws.AlignCenter($"A{lineToFillId}:F{lineToFillId}", true, false); lineToFillId++; testId++; } } + + private string BuildInstructionBatch(List instructions) + { + StringBuilder sb = new(); + foreach (var instruction in instructions) + { + sb.Append("- ").AppendLine(instruction.MainLine); + if (instruction.DocStrings.Count > 0) + { + sb.AppendLine("\""); + foreach (var docstring in instruction.DocStrings) + sb.AppendLine(docstring); + sb.AppendLine("\""); + } + } + return RemoveLastNewLine(sb.ToString()); + } + + private string RemoveLastNewLine(string s) + { + if (s.Length < 2) + return s; + return s.Substring(0, s.Length - 2); + } } } diff --git a/GherkinSimpleParser.Converter/ExcelExtensions.cs b/GherkinSimpleParser.Converter/ExcelExtensions.cs new file mode 100644 index 0000000..a5a2b84 --- /dev/null +++ b/GherkinSimpleParser.Converter/ExcelExtensions.cs @@ -0,0 +1,45 @@ +using OfficeOpenXml.Style; +using OfficeOpenXml; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace GherkinSimpleParser.Converter +{ + public static class ExcelExtensions + { + public static void SetGridBorder(this ExcelWorksheet ws, ExcelBorderStyle borderStyle, int startRow, int endRow, int startCol, int endCol) + { + for (int row = startRow; row <= endRow; row++) + { + for (int col = startCol; col <= endCol; col++) + { + GetCellAt(ws, row, col).Style.Border.BorderAround(borderStyle); + } + } + } + + public static ExcelRange GetCellAt(this ExcelWorksheet ws, int row, int col) + { + char colChar = (char)('A' - 1 + col); + return ws.Cells[$"{colChar}{row}"]; + } + + public static void SetColor(this ExcelWorksheet ws, string range, Color color) + { + ws.Cells[range].Style.Fill.PatternType = ExcelFillStyle.Solid; + ws.Cells[range].Style.Fill.BackgroundColor.SetColor(color); + } + + public static void AlignCenter(this ExcelWorksheet ws, string range, bool vertical, bool horizontal) + { + if (horizontal) + ws.Cells[range].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center; + if (vertical) + ws.Cells[range].Style.VerticalAlignment = ExcelVerticalAlignment.Center; + } + } +} diff --git a/GherkinSimpleParser.Converter/GherkinSimpleParser.Converter.csproj b/GherkinSimpleParser.Converter/GherkinSimpleParser.Converter.csproj index d75061f..95f9c57 100644 --- a/GherkinSimpleParser.Converter/GherkinSimpleParser.Converter.csproj +++ b/GherkinSimpleParser.Converter/GherkinSimpleParser.Converter.csproj @@ -5,7 +5,7 @@ enable enable True - 1.0.0 + 1.2.0 LICENSE.txt Converter to easily write Gherkin data into files README.md diff --git a/GherkinSimpleParser.Tests/ExportTests.cs b/GherkinSimpleParser.Tests/ExportTests.cs index 68267ca..db0bdd7 100644 --- a/GherkinSimpleParser.Tests/ExportTests.cs +++ b/GherkinSimpleParser.Tests/ExportTests.cs @@ -23,10 +23,10 @@ public void Setup() FeatureName = "feature name", Background = new Background { - Givens = new List + Givens = new List { - "Prerequisite_0.1", - "Prere\"q\"uisite_0.2" + new("Prerequisite_0.1"), + new("Prere\"q\"uisite_0.2") } }, Scenarios = new List @@ -34,31 +34,31 @@ public void Setup() new Scenario { Name = "Test Case 1", - Givens = new List + Givens = new List { - "Prerequisite_1.1", - "Prere\"q\"uisite_1.2" + new("Prerequisite_1.1"), + new("Prere\"q\"uisite_1.2") }, When = "Action_1", - Thens = new List + Thens = new List { - "Result_1.1", - "Resu\"l\"t_1.2" + new("Result_1.1"), + new("Resu\"l\"t_1.2") }, }, new Scenario { Name = "Test Case 2", - Givens = new List + Givens = new List { - "Prerequisite_2.1", - "Prere\"q\"uisite_2.2" + new("Prerequisite_2.1"), + new("Prere\"q\"uisite_2.2") }, When = "Action_2", - Thens = new List + Thens = new List { - "Result_2.1", - "Resu\"l\"t_2.2" + new("Result_2.1"), + new("Resu\"l\"t_2.2") } } } @@ -87,6 +87,33 @@ public void Should_export_as_CSV() csvResult); } + [Test] + public void Should_export_as_CSV_withdocstrings() + { + // Given + var converter = new CSVConverter(); + gherkinObject.Scenarios.First().Givens.First().DocStrings = new List + { + "docstrings1", + "docstrings2", + }; + + // When + var csvResult = converter.ExportAsCSV("|", gherkinObject); + + // Then + CollectionAssert.AreEqual(new List() + { + "Number;GIVEN;WHEN;THEN", + ";GENERAL PREREQUISITES:|Prerequisite_0.1|Prere\"q\"uisite_0.2;;", + "1;Test Case 1;;", + ";Prerequisite_1.1 \"docstrings1 docstrings2\"|Prere\"q\"uisite_1.2;Action_1;Result_1.1|Resu\"l\"t_1.2", + "2;Test Case 2;;", + ";Prerequisite_2.1|Prere\"q\"uisite_2.2;Action_2;Result_2.1|Resu\"l\"t_2.2" + }, + csvResult); + } + [Test] public void Should_export_as_CSV_excel_formula_wrap_french() { diff --git a/GherkinSimpleParser.Tests/ParsingTests.cs b/GherkinSimpleParser.Tests/ParsingTests.cs index 4e2b390..2f250b4 100644 --- a/GherkinSimpleParser.Tests/ParsingTests.cs +++ b/GherkinSimpleParser.Tests/ParsingTests.cs @@ -50,8 +50,8 @@ public void Should_parse_given_for_scenario() var result = GherkinObject.Parse(inputLines); // Then - Assert.That(result.Scenarios.First().Givens.First(), Is.EqualTo("prerequisite")); - Assert.That(result.Scenarios.Last().Givens.First(), Is.EqualTo("prerequisite2")); + Assert.That(result.Scenarios.First().Givens.First().MainLine, Is.EqualTo("prerequisite")); + Assert.That(result.Scenarios.Last().Givens.First().MainLine, Is.EqualTo("prerequisite2")); } [Test] @@ -72,8 +72,8 @@ public void Should_parse_given_ands_for_scenario() var result = GherkinObject.Parse(inputLines); // Then - Assert.That(result.Scenarios.First().Givens.Last(), Is.EqualTo("prerequisite1")); - Assert.That(result.Scenarios.Last().Givens.Last(), Is.EqualTo("prerequisite3")); + Assert.That(result.Scenarios.First().Givens.Last().MainLine, Is.EqualTo("prerequisite1")); + Assert.That(result.Scenarios.Last().Givens.Last().MainLine, Is.EqualTo("prerequisite3")); } [Test] @@ -112,8 +112,8 @@ public void Should_parse_then_for_scenario() var result = GherkinObject.Parse(inputLines); // Then - Assert.That(result.Scenarios.First().Thens.First(), Is.EqualTo("result")); - Assert.That(result.Scenarios.Last().Thens.First(), Is.EqualTo("result2")); + Assert.That(result.Scenarios.First().Thens.First().MainLine, Is.EqualTo("result")); + Assert.That(result.Scenarios.Last().Thens.First().MainLine, Is.EqualTo("result2")); } [Test] @@ -135,8 +135,8 @@ public void Should_parse_then_ands_for_scenario() var result = GherkinObject.Parse(inputLines); // Then - Assert.That(result.Scenarios.First().Thens.Last(), Is.EqualTo("result1")); - Assert.That(result.Scenarios.Last().Thens.Last(), Is.EqualTo("result3")); + Assert.That(result.Scenarios.First().Thens.Last().MainLine, Is.EqualTo("result1")); + Assert.That(result.Scenarios.Last().Thens.Last().MainLine, Is.EqualTo("result3")); } [Test] @@ -162,12 +162,125 @@ public void Should_parse_background_and_not_mistaken_the_ands() // Then Assert.Multiple(() => { - Assert.That(result.Background.Givens.First(), Is.EqualTo("prerequisite")); - Assert.That(result.Background.Givens.Last(), Is.EqualTo("prerequisite1")); - Assert.That(result.Scenarios.First().Givens.First(), Is.EqualTo("prerequisite2")); - Assert.That(result.Scenarios.First().Givens.Last(), Is.EqualTo("prerequisite3")); - Assert.That(result.Scenarios.First().Thens.First(), Is.EqualTo("result")); - Assert.That(result.Scenarios.Last().Thens.Last(), Is.EqualTo("result1")); + Assert.That(result.Background.Givens.First().MainLine, Is.EqualTo("prerequisite")); + Assert.That(result.Background.Givens.Last().MainLine, Is.EqualTo("prerequisite1")); + Assert.That(result.Scenarios.First().Givens.First().MainLine, Is.EqualTo("prerequisite2")); + Assert.That(result.Scenarios.First().Givens.Last().MainLine, Is.EqualTo("prerequisite3")); + Assert.That(result.Scenarios.First().Thens.First().MainLine, Is.EqualTo("result")); + Assert.That(result.Scenarios.Last().Thens.Last().MainLine, Is.EqualTo("result1")); + }); + } + + [Test] + public void Should_parse_doc_strings() + { + // Given + var inputLines = new List + { + " Background:", + " Given prerequisite", + " \"\"\"", + " docstring_bg_given_1", + " docstring_bg_given_2", + " \"\"\"", + " And prerequisite1", + " \"\"\"", + " docstring_bg_given_and_1", + " docstring_bg_given_and_2", + " \"\"\"", + " Scenario: Should do something", + " Given prerequisite2", + " \"\"\"", + " docstring_sc_given_1", + " docstring_sc_given_2", + " \"\"\"", + " And prerequisite3", + " \"\"\"", + " docstring_sc_given_and_1", + " docstring_sc_given_and_2", + " \"\"\"", + " When action", + " Then result", + " \"\"\"", + " docstring_sc_then_1", + " docstring_sc_then_2", + " \"\"\"", + " And result1", + " \"\"\"", + " docstring_sc_then_and_1", + " docstring_sc_then_and_2", + " \"\"\"", + }; + + // When + var result = GherkinObject.Parse(inputLines); + + // Then + Assert.Multiple(() => + { + CollectionAssert.AreEqual( + new List { "docstring_bg_given_1", "docstring_bg_given_2" }, + result.Background.Givens.First().DocStrings); + CollectionAssert.AreEqual( + new List { "docstring_bg_given_and_1", "docstring_bg_given_and_2" }, + result.Background.Givens.Last().DocStrings); + + CollectionAssert.AreEqual( + new List { "docstring_sc_given_1", "docstring_sc_given_2" }, + result.Scenarios.First().Givens.First().DocStrings); + CollectionAssert.AreEqual( + new List { "docstring_sc_given_and_1", "docstring_sc_given_and_2" }, + result.Scenarios.First().Givens.Last().DocStrings); + + CollectionAssert.AreEqual( + new List { "docstring_sc_then_1", "docstring_sc_then_2" }, + result.Scenarios.First().Thens.First().DocStrings); + CollectionAssert.AreEqual( + new List { "docstring_sc_then_and_1", "docstring_sc_then_and_2" }, + result.Scenarios.First().Thens.Last().DocStrings); + }); + } + + [Test] + public void Should_parse_doc_strings_and_indent() + { + // Given + var inputLines = new List + { + " Scenario: Should do something", + " Given prerequisite2", + "\"\"\"", + "docstring_sc_given_1", + " docstring_sc_given_2", + "\"\"\"", + " And prerequisite3", + " \"\"\"", + " docstring_sc_given_and_1", + " docstring_sc_given_and_2", + " \"\"\"", + " When action", + " Then result", + " \"\"\"", + " docstring_sc_then_1", + "docstring_sc_then_2", + " \"\"\"", + }; + + // When + var result = GherkinObject.Parse(inputLines); + + // Then + Assert.Multiple(() => + { + CollectionAssert.AreEqual( + new List { "docstring_sc_given_1", " docstring_sc_given_2" }, + result.Scenarios.First().Givens.First().DocStrings); + CollectionAssert.AreEqual( + new List { "docstring_sc_given_and_1", " docstring_sc_given_and_2" }, + result.Scenarios.First().Givens.Last().DocStrings); + CollectionAssert.AreEqual( + new List { "docstring_sc_then_1", "docstring_sc_then_2" }, + result.Scenarios.First().Thens.Last().DocStrings); }); } @@ -178,7 +291,6 @@ public void Should_parse_background_and_not_mistaken_the_ands() [TestCase("Scenario Template:")] [TestCase("Examples:")] [TestCase("Scenarios:")] - [TestCase("\"\"\"")] [TestCase("|")] [TestCase("@")] public void Should_throw_exception_when_encounters_an_unsupported_line(string startWith) diff --git a/GherkinSimpleParser/Background.cs b/GherkinSimpleParser/Background.cs index a51e92b..8d07011 100644 --- a/GherkinSimpleParser/Background.cs +++ b/GherkinSimpleParser/Background.cs @@ -2,6 +2,6 @@ { public class Background { - public List Givens { get; set; } = new(); + public List Givens { get; set; } = new(); } } \ No newline at end of file diff --git a/GherkinSimpleParser/GherkinObject.cs b/GherkinSimpleParser/GherkinObject.cs index c6a209b..7458058 100644 --- a/GherkinSimpleParser/GherkinObject.cs +++ b/GherkinSimpleParser/GherkinObject.cs @@ -8,69 +8,122 @@ public class GherkinObject public Background Background { get; set; } = new(); public List Scenarios { get; set; } = new(); + private enum FillingState + { + OTHER, + BACKGROUND_GIVEN, + SCENARIO_GIVEN, + SCENARIO_THEN, + } + + private class StateMachineException : Exception + { + public StateMachineException(string? message) : base(message) + { + } + } + public static GherkinObject Parse(List inputLines) { var result = new GherkinObject(); Scenario currentScenario = new(); - string andFillingState = ""; + FillingState fillingState = FillingState.OTHER; int featureCount = 0; int backgroundCount = 0; + + Queue linesStack = new Queue(inputLines); + + string currentLine; + string trimedLine; int lineCount = 0; - foreach (var line in inputLines.Select(l => l.Trim())) + while (linesStack.Count > 0) { + currentLine = linesStack.Dequeue(); lineCount++; - if (line.StartsWith("#") || string.IsNullOrEmpty(line)) + trimedLine = currentLine.Trim(); + + if (trimedLine.StartsWith("#") || string.IsNullOrEmpty(trimedLine)) { continue; } - else if (line.StartsWith("Feature: ")) + else if (trimedLine.StartsWith("Feature: ")) { if (featureCount > 0) throw new ArgumentException($"Line {lineCount}: Do not support multiple features in one file", nameof(inputLines)); - result.FeatureName = line.Substring(9); + result.FeatureName = trimedLine.Substring(9); featureCount++; + fillingState = FillingState.OTHER; } - else if (line.StartsWith("Background:")) + else if (trimedLine.StartsWith("Background:")) { if (backgroundCount > 0) throw new ArgumentException($"Line {lineCount}: Do not support multiple Background in one file", nameof(inputLines)); backgroundCount++; + fillingState = FillingState.BACKGROUND_GIVEN; } - else if (line.StartsWith("Scenario: ")) + else if (trimedLine.StartsWith("Scenario: ")) { - currentScenario = new Scenario { Name = line.Substring(10) }; + currentScenario = new Scenario { Name = trimedLine.Substring(10) }; result.Scenarios.Add(currentScenario); - andFillingState = "SCENARIO_GIVEN"; + fillingState = FillingState.SCENARIO_GIVEN; + } + else if (trimedLine.StartsWith("Given ")) + { + if (fillingState == FillingState.SCENARIO_GIVEN) + currentScenario.Givens.Add(new Instruction(trimedLine.Substring(6))); + else if(fillingState == FillingState.BACKGROUND_GIVEN) + result.Background.Givens.Add(new Instruction(trimedLine.Substring(6))); } - else if (line.StartsWith("Given ")) + else if (trimedLine.StartsWith("When ")) { - if (andFillingState == "SCENARIO_GIVEN") - currentScenario.Givens.Add(line.Substring(6)); - else - result.Background.Givens.Add(line.Substring(6)); + currentScenario.When = trimedLine.Substring(5); } - else if (line.StartsWith("When ")) + else if (trimedLine.StartsWith("Then ")) { - currentScenario.When = line.Substring(5); + currentScenario.Thens.Add(new Instruction(trimedLine.Substring(5))); + fillingState = FillingState.SCENARIO_THEN; } - else if (line.StartsWith("Then ")) + else if (trimedLine.StartsWith("And ")) { - currentScenario.Thens.Add(line.Substring(5)); - andFillingState = "SCENARIO_THEN"; + if (fillingState == FillingState.SCENARIO_GIVEN) + currentScenario.Givens.Add(new Instruction(trimedLine.Substring(4))); + else if (fillingState == FillingState.SCENARIO_THEN) + currentScenario.Thens.Add(new Instruction(trimedLine.Substring(4))); + else if (fillingState == FillingState.BACKGROUND_GIVEN) + result.Background.Givens.Add(new Instruction(trimedLine.Substring(4))); } - else if (line.StartsWith("And ")) + else if(trimedLine.StartsWith("\"\"\"")) { - if (andFillingState == "SCENARIO_GIVEN") - currentScenario.Givens.Add(line.Substring(4)); - else if (andFillingState == "SCENARIO_THEN") - currentScenario.Thens.Add(line.Substring(4)); - else - result.Background.Givens.Add(line.Substring(4)); + int indentCountReference = currentLine.IndexOf('"'); + currentLine = linesStack.Dequeue(); + lineCount++; + while (!currentLine.Trim().StartsWith("\"\"\"")) + { + int firstCharIndex = string.IsNullOrEmpty(currentLine) ? 0 : currentLine.IndexOf(currentLine.Trim().First()); + int leadingZerosToAdd = firstCharIndex - indentCountReference >= 0 ? firstCharIndex - indentCountReference : 0; + string indentedLine = new string(' ', leadingZerosToAdd) + currentLine.Trim(); + switch (fillingState) + { + case FillingState.BACKGROUND_GIVEN: + result.Background.Givens.Last().DocStrings.Add(indentedLine); + break; + case FillingState.SCENARIO_GIVEN: + currentScenario.Givens.Last().DocStrings.Add(indentedLine); + break; + case FillingState.SCENARIO_THEN: + currentScenario.Thens.Last().DocStrings.Add(indentedLine); + break; + default: + throw new StateMachineException($"State {fillingState} is not allowed to contain DocString"); + } + currentLine = linesStack.Dequeue(); + lineCount++; + } } else { - throw new ArgumentException($"Line {lineCount}: Unsupported line: \"{line}\"", nameof(inputLines)); + throw new ArgumentException($"Line {lineCount}: Unsupported line: \"{trimedLine}\"", nameof(inputLines)); } } diff --git a/GherkinSimpleParser/GherkinSimpleParser.csproj b/GherkinSimpleParser/GherkinSimpleParser.csproj index c34c6c5..8d9f24a 100644 --- a/GherkinSimpleParser/GherkinSimpleParser.csproj +++ b/GherkinSimpleParser/GherkinSimpleParser.csproj @@ -5,7 +5,7 @@ enable enable True - 1.1.0 + 1.2.0 Gherkin Simple Parser to object Servan CHARLOT README.md diff --git a/GherkinSimpleParser/Instruction.cs b/GherkinSimpleParser/Instruction.cs new file mode 100644 index 0000000..f9e4746 --- /dev/null +++ b/GherkinSimpleParser/Instruction.cs @@ -0,0 +1,13 @@ +namespace GherkinSimpleParser +{ + public class Instruction + { + public Instruction(string mainLine) + { + MainLine = mainLine; + } + + public string MainLine { get; set; } + public List DocStrings { get; set; } = new(); + } +} \ No newline at end of file diff --git a/GherkinSimpleParser/Scenario.cs b/GherkinSimpleParser/Scenario.cs index 8065d68..afea3fc 100644 --- a/GherkinSimpleParser/Scenario.cs +++ b/GherkinSimpleParser/Scenario.cs @@ -3,8 +3,8 @@ public class Scenario { public string Name { get; set; } - public List Givens { get; set; } = new(); + public List Givens { get; set; } = new(); public string When { get; set; } - public List Thens { get; set; } = new(); + public List Thens { get; set; } = new(); } } \ No newline at end of file diff --git a/README.md b/README.md index dbb1209..c990473 100644 --- a/README.md +++ b/README.md @@ -20,21 +20,21 @@ var gherkinObj = GherkinObject.Parse(inputLines)); * `When` (unique) * `Then` * `And` (then, multiple) +* `"""` (Doc Strings) (for Given, Then) ## Ignores * Empty lines * `#` (Comments) -## DO NOT support -* Guard clause for wrongly structured files -* Unexpected lines (will throw exception) +## DO NOT support (will throw exception) +* Guard clause for wrongly structured files and unexpected lines * Multiple `Feature` per file +* `Rule` * `Example` * `But` * `*` * `Scenario Outline` (or `Scenario Template`) * `Examples` (or `Scenarios`) -* `"""` (Doc Strings) * `|` (Data Tables) * `@` (Tags) @@ -46,6 +46,10 @@ Using the applicaiton extension `GherkinSimpleParser.Converter` you have access ## Export as CSV +### To be noted + +New lines and carriage return are removed from Doc strings (`"""`). + ### Export as CSV for testplan with \ for ANDs Using the applicaiton extension `GherkinSimpleParser.Converter` you have access to the class `CSVConverter` that does the following: @@ -93,7 +97,7 @@ into ### Export as CSV for testplan in Excel with formula wrap -**LIMITATION: GIVENs and THENs text can only be 255 character longs because excel is annoying.** +**LIMITATION: GIVENs and THENs text can only be 255 character longs because excel is annoying. Hence, DocStrings are not exported in this mode** ```csharp GherkinObject gherkinObj = GherkinObject.Parse(inputLines));