Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* New Project Wizard references the latest versions of supported test frameworks and .NET frameworks
* Format Document will now right-align numeric values in tables. This can be overridden to left align them by setting `gherkin_table_cell_right_align_numeric_content = false` in .editorconfig file within a `[*.feature]` section.
* Define Steps Command now supports generating async method definitions.

## Bug fixes:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<ComboBoxItem Tag="RegularExpression">Regular Expressions</ComboBoxItem>
<ComboBoxItem Tag="CucumberExpression">Cucumber Expressions</ComboBoxItem>
</ComboBox>-->
<CheckBox Grid.Column="1" Grid.Row="1" Margin="0,8,0,8"
Content="Use asynchronous method declarations."
IsChecked="{Binding GenerateAsyncMethods, Mode=TwoWay}" />
<StackPanel Grid.Column="2" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right"
Margin="0,3,12,6">
<Button Style="{StaticResource VsDialogButton}" Click="CopyToClipboard_Click">Copy to clipboard</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class DeveroomConfiguration
public string ConfiguredBindingCulture { get; set; } = null;
public string BindingCulture => ConfiguredBindingCulture ?? DefaultFeatureLanguage;
public SnippetExpressionStyle SnippetExpressionStyle { get; set; } = SnippetExpressionStyle.CucumberExpression;

public bool GenerateAsyncSkeletonMethods { get; set; } = true;

private void FixEmptyContainers()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public void Populate(string jsonString, DeveroomConfiguration config)
if (sdSnippetStyle == "RegexAttribute")
config.SnippetExpressionStyle = SnippetExpressionStyle.RegularExpression;
}
if (reqnrollJsonConfiguration.Trace != null &&
reqnrollJsonConfiguration.Trace.TryGetValue("generateStepDefinitionSkeletonAsAsync", out var generateStepDefinitionSkeletonAsAsync) &&
bool.TryParse(generateStepDefinitionSkeletonAsAsync, out bool generateAsyncSkeletonMethods))
{
config.GenerateAsyncSkeletonMethods = generateAsyncSkeletonMethods;
}
}

private class ReqnrollJsonConfiguration
Expand Down
51 changes: 34 additions & 17 deletions Reqnroll.VisualStudio/Editor/Commands/DefineStepsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,22 @@ public override bool PreExec(IWpfTextView textView, DeveroomEditorCommandTargetK
const string indent = " ";
string newLine = Environment.NewLine;

var feature = (Feature) featureTag.Data;
var viewModel = new CreateStepDefinitionsDialogViewModel();
viewModel.ClassName = feature.Name.ToIdentifier() + "StepDefinitions";
viewModel.ExpressionStyle = snippetService.DefaultExpressionStyle;

foreach (var undefinedStepTag in undefinedStepTags)
var feature = (Feature)featureTag.Data;
var viewModel = new CreateStepDefinitionsDialogViewModel
{
var matchResult = (MatchResult) undefinedStepTag.Data;
foreach (var match in matchResult.Items.Where(mi => mi.Type == MatchResultType.Undefined))
{
var snippet = snippetService.GetStepDefinitionSkeletonSnippet(match.UndefinedStep,
viewModel.ExpressionStyle, indent, newLine);
if (viewModel.Items.Any(i => i.Snippet == snippet))
continue;

viewModel.Items.Add(new StepDefinitionSnippetItemViewModel {Snippet = snippet});
}
}
IsInitializing = true,
Generator = GenerateSnippets,
ClassName = feature.Name.ToIdentifier() + "StepDefinitions",
ExpressionStyle = snippetService.DefaultExpressionStyle,
GenerateAsyncMethods = snippetService.DefaultGenerateSkeletonMethodsAsAsync,
Indent = indent,
NewLine = newLine,
UndefinedStepTags = undefinedStepTags,
SnippetService = snippetService
};
viewModel.IsInitializing = false;

viewModel.Items = new ObservableCollection<StepDefinitionSnippetItemViewModel>(GenerateSnippets(viewModel));

IdeScope.WindowManager.ShowDialog(viewModel);

Expand Down Expand Up @@ -113,6 +111,25 @@ public override bool PreExec(IWpfTextView textView, DeveroomEditorCommandTargetK
return true;
}

public IEnumerable<StepDefinitionSnippetItemViewModel> GenerateSnippets(CreateStepDefinitionsDialogViewModel viewModel)
{
var result = new List<StepDefinitionSnippetItemViewModel>();
foreach (var undefinedStepTag in viewModel.UndefinedStepTags)
{
var matchResult = (MatchResult)undefinedStepTag.Data;
foreach (var match in matchResult.Items.Where(mi => mi.Type == MatchResultType.Undefined))
{
var snippet = viewModel.SnippetService.GetStepDefinitionSkeletonSnippet(match.UndefinedStep,
viewModel.ExpressionStyle, viewModel.GenerateAsyncMethods, viewModel.Indent, viewModel.NewLine);
if (result.Any(i => i.Snippet == snippet))
continue;

result.Add(new StepDefinitionSnippetItemViewModel { Snippet = snippet });
}
}
return result;
}

private void SaveAsStepDefinitionClass(IProjectScope projectScope, string combinedSnippet, string className,
string indent, string newLine)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ private void PerformOfferCopySnippet(MatchResultItem match, ITextBuffer textBuff
string newLine = Environment.NewLine;

var snippet = snippetService.GetStepDefinitionSkeletonSnippet(match.UndefinedStep,
snippetService.DefaultExpressionStyle, indent, newLine);
snippetService.DefaultExpressionStyle, snippetService.DefaultGenerateSkeletonMethodsAsAsync, indent, newLine);

IdeScope.Actions.ShowQuestion(new QuestionDescription(GoToStepDefinitionsPopupHeader,
$"The step is undefined. Do you want to copy a step definition skeleton snippet to the clipboard?{Environment.NewLine}{Environment.NewLine}{snippet}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ protected DeveroomStepDefinitionSkeletonProvider(ReqnrollProjectTraits projectTr
}

public string GetStepDefinitionSkeletonSnippet(UndefinedStepDescriptor undefinedStep,
string indent, string newLine, string bindingCultureName)
string indent, bool generateAsyncSnippet, string newLine, string bindingCultureName)
{
var bindingCulture = CultureInfo.GetCultureInfo(bindingCultureName);

Expand All @@ -23,9 +23,10 @@ public string GetStepDefinitionSkeletonSnippet(UndefinedStepDescriptor undefined
var methodName = GetMethodName(undefinedStep, analyzedStepText);
var parameters = string.Join(", ", analyzedStepText.Parameters.Select(ToDeclaration));
var stringPrefix = UseVerbatimStringForExpression ? "@" : "";
var returnSignature = generateAsyncSnippet ? "async Task" : "void";

var method = $"[{undefinedStep.ScenarioBlock}({stringPrefix}\"{regex}\")]" + newLine +
$"public void {methodName}({parameters})" + newLine +
$"public {returnSignature} {methodName}{(generateAsyncSnippet ? "Async" : "")}({parameters})" + newLine +
"{" + newLine +
$"{indent}throw new PendingStepException();" + newLine +
"}" + newLine;
Expand Down
5 changes: 3 additions & 2 deletions Reqnroll.VisualStudio/Snippets/SnippetService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ public SnippetService(IProjectScope projectScope)

public SnippetExpressionStyle DefaultExpressionStyle =>
_projectScope.GetDeveroomConfiguration().SnippetExpressionStyle;
public bool DefaultGenerateSkeletonMethodsAsAsync => _projectScope.GetDeveroomConfiguration().GenerateAsyncSkeletonMethods;

public string GetStepDefinitionSkeletonSnippet(UndefinedStepDescriptor undefinedStep,
SnippetExpressionStyle expressionStyle, string indent = " ", string newLine = null)
SnippetExpressionStyle expressionStyle, bool generateAsyncSkeletonSnippet, string indent = " ", string newLine = null)
{
try
{
Expand All @@ -28,7 +29,7 @@ public string GetStepDefinitionSkeletonSnippet(UndefinedStepDescriptor undefined
var configuration = _projectScope.GetDeveroomConfiguration();
newLine = newLine ?? Environment.NewLine;
var result =
skeletonProvider.GetStepDefinitionSkeletonSnippet(undefinedStep, indent, newLine,
skeletonProvider.GetStepDefinitionSkeletonSnippet(undefinedStep, indent, generateAsyncSkeletonSnippet, newLine,
configuration.BindingCulture);
_logger.LogInfo(
$"Step definition snippet generated for step '{undefinedStep.StepText}': {Environment.NewLine}{result}");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
#nullable disable
using Reqnroll.VisualStudio.Snippets;
using System;
using System.Linq;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;

namespace Reqnroll.VisualStudio.UI.ViewModels;

public class CreateStepDefinitionsDialogViewModel
public class CreateStepDefinitionsDialogViewModel : INotifyPropertyChanged
{
#if DEBUG
public static CreateStepDefinitionsDialogViewModel DesignData = new()
{
ClassName = "MyFeatureSteps",
ExpressionStyle = SnippetExpressionStyle.CucumberExpression,
Items = new List<StepDefinitionSnippetItemViewModel>
Items = new ObservableCollection<StepDefinitionSnippetItemViewModel>
{
new()
{
Expand All @@ -33,15 +37,89 @@ public void GivenThereIsASimpleReqnrollProjectForVersion(Version reqnrollVersion
{
Snippet = @"[When(@""there is a simple Reqnroll project for (.*)"")]
public void GivenThereIsASimpleReqnrollProjectForVersion(Version reqnrollVersion)
{
throw new PendingStepException();
}"
}
}
};
public static CreateStepDefinitionsDialogViewModel DesignDataAsync = new()
{
ClassName = "MyFeatureSteps",
ExpressionStyle = SnippetExpressionStyle.CucumberExpression,
Items = new ObservableCollection<StepDefinitionSnippetItemViewModel>
{
new()
{
Snippet = @"[Given(@""there is a simple Reqnroll project for (.*)"")]
public async Task GivenThereIsASimpleReqnrollProjectForVersionAsync(Version reqnrollVersion)
{
throw new PendingStepException();
}"
},
new()
{
Snippet = @"[When(@""there is a simple Reqnroll project for (.*)"")]
public async Task GivenThereIsASimpleReqnrollProjectForVersionAsync(Version reqnrollVersion)
{
throw new PendingStepException();
}"
},
new()
{
Snippet = @"[When(@""there is a simple Reqnroll project for (.*)"")]
public async Task GivenThereIsASimpleReqnrollProjectForVersionAsync(Version reqnrollVersion)
{
throw new PendingStepException();
}"
}
}
};
#endif

public string ClassName { get; set; }
public SnippetExpressionStyle ExpressionStyle { get; set; }
public List<StepDefinitionSnippetItemViewModel> Items { get; set; } = new();
private bool _generateAsyncMethods = true;
public bool GenerateAsyncMethods
{
get => _generateAsyncMethods;
set
{
if (_generateAsyncMethods != value)
{
_generateAsyncMethods = value;
OnPropertyChanged(nameof(GenerateAsyncMethods));
if (!IsInitializing)
{
RegenerateItems(); // Regenerate when property changes
}
}
}
}
public ObservableCollection<StepDefinitionSnippetItemViewModel> Items { get; set; } = new();
public CreateStepDefinitionsDialogResult Result { get; set; }
public Func<CreateStepDefinitionsDialogViewModel, IEnumerable<StepDefinitionSnippetItemViewModel>> Generator { get; set; }
public DeveroomTag[] UndefinedStepTags { get; set; }
public SnippetService SnippetService { get; set; }
public string Indent { get; set; }
public string NewLine { get; set; }

public bool IsInitializing;

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public void RegenerateItems()
{
Items.Clear();
foreach (var item in Generator(this))
{
Items.Add(item);
}
OnPropertyChanged(nameof(Items));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Scenario: Two undefined step has the same step definition skeleton
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| type | expression |
| type | expression |
| Given | the operand {int} has been entered |

Scenario: All steps are defined
Expand Down Expand Up @@ -121,11 +121,11 @@ Scenario: DefineSteps command abides by reqnroll.json configuration for regex sk
"trace": { "stepDefinitionSkeletonStyle": "RegexAttribute" }
}
"""
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| type | expression |
| Given | the client added (.*) pcs to the basket |
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| type | expression |
| Given | the client added (.*) pcs to the basket |

Scenario: DefineSteps command properly escapes empty brackets when using Cucumber expressions
Given there is a Reqnroll project scope
Expand All @@ -139,8 +139,8 @@ Scenario: DefineSteps command properly escapes empty brackets when using Cucumbe
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| type | expression |
| When | I use \\(parenthesis), \\{curly braces} and\\/or \\\ backslash |
| type | expression |
| When | I use \\(parenthesis), \\{curly braces} and\\/or \\\\ backslash |

Scenario: DefineSteps command properly escapes empty brackets when using Regex expressions
Given there is a Reqnroll project scope
Expand All @@ -160,6 +160,48 @@ Scenario: DefineSteps command properly escapes empty brackets when using Regex e
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| type | expression |
| When | I use \\(parenthesis\), \\{curly braces}, \\\ backslash, and/or \\. period |
| type | expression |
| When | I use \\(parenthesis\\), \\{curly braces}, \\\\ backslash, and/or \\. period |

Scenario: DefineSteps command abides by reqnroll.json configuration for async method declaration
Given there is a Reqnroll project scope
And the following feature file in the editor
"""
Feature: Feature Using Regex Style

Scenario: Client has a simple basket
Given the client has a basket
"""
And the reqnroll.json configuration file contains
"""
{
"trace": { "generateStepDefinitionSkeletonAsAsync": true }
}
"""
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| Method |
| MyProject.StepDefinitions1.GivenTheClientHasABasketAsync |

Scenario: DefineSteps command abides by reqnroll.json configuration for synchronous method declaration
Given there is a Reqnroll project scope
And the following feature file in the editor
"""
Feature: Feature Using Regex Style

Scenario: Client has a simple basket
Given the client has a basket
"""
And the reqnroll.json configuration file contains
"""
{
"trace": { "generateStepDefinitionSkeletonAsAsync": false }
}
"""
And the project is built and the initial binding discovery is performed
When I invoke the "Define Steps" command
Then the define steps dialog should be opened with the following step definition skeletons
| Method |
| MyProject.StepDefinitions1.GivenTheClientHasABasket |

Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,8 @@ private StepDefinitionSnippetData[] ParseSnippetsFromFile(string text,
{
Type = sd.Type,
Regex = sd.Regex,
Expression = sd.Expression
Expression = sd.Expression,
Method = sd.Method
}).ToArray();
}

Expand Down Expand Up @@ -987,5 +988,6 @@ private class StepDefinitionSnippetData
public string Type { get; set; }
public string Regex { get; set; }
public string Expression { get; set; }
public string Method { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,19 @@
<ItemGroup>
<ProjectReference Include="..\..\Reqnroll.VisualStudio.UI\Reqnroll.VisualStudio.UI.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>

<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>
Loading
Loading