Skip to content
Merged
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
19 changes: 19 additions & 0 deletions src/.editorconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
[*.cs]

# Static readonly fields should be PascalCase
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.symbols = static_readonly_fields
dotnet_naming_rule.static_readonly_fields_should_be_pascal_case.style = pascal_case_style

dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field
dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = *
dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly

dotnet_naming_style.pascal_case_style.capitalization = pascal_case

# Constants should be PascalCase
dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
dotnet_naming_rule.constants_should_be_pascal_case.style = pascal_case_style

dotnet_naming_symbols.constants.applicable_kinds = field
dotnet_naming_symbols.constants.applicable_accessibilities = *
dotnet_naming_symbols.constants.required_modifiers = const

# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
dotnet_diagnostic.SYSLIB1045.severity = none
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public void SuccessfulRailCardPurchases()
.And(_ => TheBuyerSelectsA(fare))
.When(_ => TheBuyerPays())
.Then(_ => ASaleOccursWithAnAmountOf(Price))
.WithExamples(new ExampleTable(
"Buyer Category", "Fare", "Price")
.WithExamples(new ExampleTable("Buyer Category", "Fare", "Price")
{
{BuyerCategory.Student, new MonthlyPass(), new Money(76)},
{BuyerCategory.Senior, new MonthlyPass(), new Money(98)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal void AndGivenTwoStoriesEachWithTwoScenariosWithThreeStepsOfFiveMillisec

internal void WhenTheDiagnosticDataIsCalculated()
{
_result = _sut.GetDiagnosticData(new FileReportModel(_stories.ToReportModel()));
_result = DiagnosticsReportBuilder.GetDiagnosticData(new FileReportModel(_stories.ToReportModel()));
}

internal void ThenTwoStoriesShouldBeReturned()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Scenario: Reflective with examples
Given step with <first example> passed as parameter
And step with <second example> accessed via property

Examples:
| First Example | Second Example |
| 1 | foo |
| 2 | bar |

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using Shouldly;
using System.Diagnostics.CodeAnalysis;
using TestStack.BDDfy.Reporters;
using Xunit;

namespace TestStack.BDDfy.Tests.Scanner.Examples
{
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public class ReflectiveWithExamples
{
private readonly Story _story;
Expand All @@ -20,18 +22,12 @@ public ReflectiveWithExamples()
.BDDfy();
}

internal void GivenStepWith__FirstExample__PassedAsParameter(int firstExample)
{
firstExample.ShouldBeOneOf(1, 2);
}
internal void GivenStepWith__FirstExample__PassedAsParameter(int firstExample) => firstExample.ShouldBeOneOf(1, 2);

internal void AndGivenStepWith__SecondExample__AccessedViaProperty()
{
SecondExample.ShouldBeOneOf("foo", "bar");
}
internal void AndGivenStepWith__SecondExample__AccessedViaProperty() => SecondExample.ShouldBeOneOf("foo", "bar");

[Fact]
public void Run()
public void RunExamplesUsingReflectiveScanner()
{
var reporter = new TextReporter();
reporter.Process(_story);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using Shouldly;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using TestStack.BDDfy.Configuration;
using Xunit;

namespace TestStack.BDDfy.Tests.Scanner.ReflectiveScanner
{
[SuppressMessage("Performance", "CA1822:Mark members as static")]
public class AnyExecutableAttributeWhichIsEmptyOrWhitespaceWillResultInMethodName
{
private class TypeWithDecoratedMethods
Expand All @@ -20,8 +22,10 @@ public TypeWithDecoratedMethods()

[Given("")]
public void GivenWithEmptyString() { }

[When(" ")]
public void WhenWithWhitespace() { }

[Then(null)]
public void ThenWithNull() { }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Runtime.CompilerServices;
using TestStack.BDDfy.Configuration;
using TestStack.BDDfy.Tests;

namespace TestStack.BDDfy.Samples
namespace TestStack.BDDfy.Tests
{
public class Startup
{
Expand Down
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy.Tests/XUnitOutputReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace TestStack.BDDfy.Tests
{
public class XUnitOutputReporter(ITestOutputHelper testOutputHelper = null): TextReporter
{
private ITestOutputHelper _outputHelper = testOutputHelper ?? Xunit.TestContext.Current.TestOutputHelper;
private readonly ITestOutputHelper _outputHelper = testOutputHelper ?? Xunit.TestContext.Current.TestOutputHelper;

protected override void WriteLine(string text = null)
{
Expand Down
18 changes: 10 additions & 8 deletions src/TestStack.BDDfy/Abstractions/DefaultStepTitleFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ string createTitle()
if (testContext.Examples != null)
{
var matchingHeaders = testContext.Examples.Headers
.Where(header => ExampleTable.HeaderMatches(header, i.ParameterName) ||
ExampleTable.HeaderMatches(header, i.Value.Name))
.Where(header => ExampleTable.HeaderMatches(header, i.ParameterName) || ExampleTable.HeaderMatches(header, i.Value.Name))
.ToList();

if (matchingHeaders.Count > 1)
Expand All @@ -58,7 +57,7 @@ string createTitle()
if (matchingHeader != null)
return string.Format("<{0}>", matchingHeader);
}
return i.Value.Value.FlattenArray();
return i.Value.Value?.FlattenArray() ?? Array.Empty<string>();
})
.ToArray();

Expand All @@ -73,14 +72,17 @@ string createTitle()

public StepTitle Create(string title, string stepPrefix, ITestContext testContext) => new(AppendPrefix(title, stepPrefix));

private static string AppendPrefix(string title, string stepPrefix)
private static string AppendPrefix(string? title, string stepPrefix)
{
if (!title.StartsWith(stepPrefix, StringComparison.CurrentCultureIgnoreCase))
var stepTitle = (title ?? string.Empty).Trim();

if (!stepTitle.StartsWith(stepPrefix, StringComparison.CurrentCultureIgnoreCase))
{
if (title.Length == 0) return string.Format("{0} ", stepPrefix);
return string.Format("{0} {1}{2}", stepPrefix, title[..1].ToLower(), title[1..]);
if (stepTitle.Length == 0) return string.Format("{0} ", stepPrefix);

return string.Format("{0} {1}{2}", stepPrefix, stepTitle[..1].ToLower(), stepTitle[1..]);
}

return title;
return stepTitle;
}
}
24 changes: 12 additions & 12 deletions src/TestStack.BDDfy/BDDfyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,19 @@ public static class BDDfyExtensions
/// <returns></returns>
public static Story BDDfy(
this object testObject,
string scenarioTitle = null,
string? scenarioTitle = null,
[System.Runtime.CompilerServices.CallerMemberName]
string caller = null)
string? caller = null)
{
var callerName = testObject.GetActualCallerName(caller);
return InternalLazyBDDfy(testObject, scenarioTitle ?? Configurator.Humanizer.Humanize(callerName)).Run();
}

public static Engine LazyBDDfy(
this object testObject,
string scenarioTitle = null,
string? scenarioTitle = null,
[System.Runtime.CompilerServices.CallerMemberName]
string caller = null)
string? caller = null)
{
var callerName = testObject.GetActualCallerName(caller);
return InternalLazyBDDfy(testObject, scenarioTitle ?? Configurator.Humanizer.Humanize(callerName));
Expand All @@ -43,9 +43,9 @@ public static Engine LazyBDDfy(
/// <returns></returns>
public static Story BDDfy<TStory>(
this object testObject,
string scenarioTitle = null,
string? scenarioTitle = null,
[System.Runtime.CompilerServices.CallerMemberName]
string caller = null)
string? caller = null)
where TStory : class
{
var callerName = testObject.GetActualCallerName(caller);
Expand All @@ -54,9 +54,9 @@ public static Story BDDfy<TStory>(

public static Engine LazyBDDfy<TStory>(
this object testObject,
string scenarioTitle = null,
string? scenarioTitle = null,
[System.Runtime.CompilerServices.CallerMemberName]
string caller = null)
string? caller = null)
where TStory : class
{
var callerName = testObject.GetActualCallerName(caller);
Expand All @@ -65,8 +65,8 @@ public static Engine LazyBDDfy<TStory>(

static Engine InternalLazyBDDfy(
object testObject,
string scenarioTitle,
Type explicitStoryType = null)
string? scenarioTitle,
Type? explicitStoryType = null)
{
var testContext = TestContext.GetContext(testObject);

Expand All @@ -77,15 +77,15 @@ static Engine InternalLazyBDDfy(
return new (storyScanner);
}

static DefaultScanner GetReflectiveScanner(ITestContext testContext, string scenarioTitle = null, Type explicitStoryType = null)
static DefaultScanner GetReflectiveScanner(ITestContext testContext, string? scenarioTitle = null, Type? explicitStoryType = null)
{
var stepScanners = Configurator.Scanners.GetStepScanners(testContext).ToArray();
var reflectiveScenarioScanner = new ReflectiveScenarioScanner(scenarioTitle, stepScanners);

return new (testContext, reflectiveScenarioScanner, explicitStoryType);
}

static string GetActualCallerName(this object testObject, string inferedCallerName)
static string? GetActualCallerName(this object testObject, string? inferedCallerName)
=> inferedCallerName == ".ctor" ? testObject.GetType().Name : inferedCallerName;
}
}
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy/Configuration/ComponentFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void Enable()
_active = true;
}

public TComponent ConstructFor(TMaterial material)
public TComponent? ConstructFor(TMaterial material)
{
if (_active && _runsOn(material))
return _factory();
Expand Down
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy/Configuration/IHumanizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace TestStack.BDDfy.Configuration
{
public interface IHumanizer
{
string Humanize(string input);
string? Humanize(string? input);
}
}
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy/Configuration/Scanners.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ public IEnumerable<IStepScanner> GetStepScanners(object objectUnderTest)
public Func<IStoryMetadataScanner> StoryMetadataScanner = () => new StoryAttributeMetadataScanner();

[Obsolete("This will be removed soon. Use Configurator.Humanizer.Humanize")]
public static Func<string, string> Humanize = Configurator.Humanizer.Humanize;
public static Func<string, string?> Humanize = Configurator.Humanizer.Humanize;
}
}
4 changes: 2 additions & 2 deletions src/TestStack.BDDfy/DefaultHumanizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ internal partial class DefaultHumanizer: IHumanizer
private static readonly Regex UnicodeMatchPattern = new(@"[^\u0000-\u007F]");
private static readonly Regex LoneIReplacePattern = new(@"(?<=^|\s)i(?=\s|$)");

public string Humanize(string input)
public string? Humanize(string? input)
{
if (string.IsNullOrWhiteSpace(input)) return input;

var shouldPreserveCasing = input.Replace("__", "-").Contains('_');

input = TokensPattern.Replace(input, "-#$1#-");

var words = input.Split(['_','-']);
var words = input.Split(['_', '-'], StringSplitOptions.RemoveEmptyEntries);

var finalWords = words.Select(x => TokenReplacePattern.Replace(x, "<$1>"));

Expand Down
16 changes: 9 additions & 7 deletions src/TestStack.BDDfy/Engine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;
using TestStack.BDDfy.Configuration;
using TestStack.BDDfy.Processors;

Expand All @@ -7,6 +8,7 @@ namespace TestStack.BDDfy
public class Engine(IScanner scanner)
{
private readonly IScanner _scanner = scanner;
private Story? _story;

static Engine()
{
Expand All @@ -23,25 +25,25 @@ static void InvokeBatchProcessors()

public Story Run()
{
Story = _scanner.Scan();
_story = _scanner.Scan();

var processors = Configurator.Processors.GetProcessors(Story).ToList();
var processors = Configurator.Processors.GetProcessors(_story).ToList();

try
{
//run processors in the right order regardless of the order they are provided to the Bddfier
foreach (var processor in processors.Where(p => p.ProcessType < ProcessType.Disposal).OrderBy(p => (int)p.ProcessType))
processor.Process(Story);
processor.Process(_story);
}
finally
{
foreach (var finallyProcessor in processors.Where(p => p.ProcessType >= ProcessType.Disposal).OrderBy(p => (int)p.ProcessType))
finallyProcessor.Process(Story);
finallyProcessor.Process(_story);
}

return Story;
return _story;
}

public Story Story { get; private set; }
public Story Story { get { return _story ?? throw new InvalidOperationException("Story has not been run yet"); } }
}
}
10 changes: 10 additions & 0 deletions src/TestStack.BDDfy/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Justification = "We want most of the classes to sit in root namespace", Scope = "namespace", Target = "~N:TestStack.BDDfy")]
[assembly: SuppressMessage("Design", "CA1069:Enums values should not be duplicated", Scope = "type", Target = "~T:TestStack.BDDfy.Annotations.ImplicitUseTargetFlags")]
[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Scope = "namespace", Target = "~N:TestStack.BDDfy.Annotations")]
2 changes: 1 addition & 1 deletion src/TestStack.BDDfy/IStepExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace TestStack.BDDfy
{
public interface IStepExecutor
{
object Execute(Step step, object testObject);
object? Execute(Step step, object testObject);
}
}
4 changes: 2 additions & 2 deletions src/TestStack.BDDfy/ITestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace TestStack.BDDfy
public interface ITestContext
{
object TestObject { get; }
ExampleTable Examples { get; set; }
IFluentScanner FluentScanner { get; set; }
ExampleTable? Examples { get; set; }
IFluentScanner? FluentScanner { get; set; }
List<string> Tags { get; }
}
}
Loading
Loading