diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 3cdfa0f..e063a44 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -1,42 +1,4 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow will build, test, sign and package a WPF or Windows Forms desktop application -# built on .NET Core. -# To learn how to migrate your existing application to .NET Core, -# refer to https://docs.microsoft.com/en-us/dotnet/desktop-wpf/migration/convert-project-from-net-framework -# -# To configure this workflow: -# -# 1. Configure environment variables -# GitHub sets default environment variables for every workflow run. -# Replace the variables relative to your project in the "env" section below. -# -# 2. Signing -# Generate a signing certificate in the Windows Application -# Packaging Project or add an existing signing certificate to the project. -# Next, use PowerShell to encode the .pfx file using Base64 encoding -# by running the following Powershell script to generate the output string: -# -# $pfx_cert = Get-Content '.\SigningCertificate.pfx' -Encoding Byte -# [System.Convert]::ToBase64String($pfx_cert) | Out-File 'SigningCertificate_Encoded.txt' -# -# Open the output file, SigningCertificate_Encoded.txt, and copy the -# string inside. Then, add the string to the repo as a GitHub secret -# and name it "Base64_Encoded_Pfx." -# For more information on how to configure your signing certificate for -# this workflow, refer to https://github.com/microsoft/github-actions-for-desktop-apps#signing -# -# Finally, add the signing certificate password to the repo as a secret and name it "Pfx_Key". -# See "Build the Windows Application Packaging project" below to see how the secret is used. -# -# For more information on GitHub Actions, refer to https://github.com/features/actions -# For a complete CI/CD sample to get started with GitHub Action workflows for Desktop Applications, -# refer to https://github.com/microsoft/github-actions-for-desktop-apps - -name: .NET Core Desktop +name: .NET Core on: push: @@ -56,7 +18,7 @@ jobs: runs-on: ubuntu-latest env: - Version: 1.0.${{ github.run_number }} + Version: 1.1.${{ github.run_number }} Solution_Path: ./src/GherXunit.sln Project_Path: ./src/lib/GherXunit/GherXunit.csproj Package_Path: ./src/lib/GherXunit/**/*. diff --git a/README.md b/README.md index 1b487dd..3dedc69 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ namespace BddTests.Samples.Features; public partial class SubscriptionTest(ITestOutputHelper output): IGherXunit { public ITestOutputHelper Output { get; } = output; - private void WhenPattyLogsSteps() { } + private void WhenPattyLogsSteps() => Assert.True(true); private async Task WhenFriedaLogsSteps() => await Task.CompletedTask; } ``` @@ -87,17 +87,21 @@ public partial class SubscriptionTest(ITestOutputHelper output): IGherXunit #### 📌 Example of output highlighting the test results: The result of running the test scenarios defined in the `SubscriptionTest` class would be similar to the following output: -```gherkindotnet -✅ Dr. Bill posts to his own blog -GIVEN a global administrator named <<"Greg">> - AND a blog named <<"Greg's anti-tax rants">> - AND a customer named <<"Dr. Bill">> - AND a blog named <<"Expensive Therapy">> owned by <<"Dr. Bill">> - -✅ Dr. Bill posts to his own blog -GIVEN I am logged in as Dr. Bill - WHEN I try to post to "Expensive Therapy" - THEN I should see "Your article was published." +```shell +11:11:49.683 |V| Run: 0821bae4-1a1a-447b-807e-2eb5042f1fe5 - Discovery result processing started +11:11:49.688 |V| Run: 0821bae4-1a1a-447b-807e-2eb5042f1fe5 - Discovery result processing finished: (+0 ~2 -0), interrupted: 0 +11:11:49.879 |I| Process /usr/local/share/dotnet/x64/dotnet:21042 has exited with code (0) +11:11:49.879 |I| Output stream: + +SCENARIO ⇲ [🟢]Free subscribers see only the free articles + GIVEN ⇲ Free Frieda has a free subscription + WHEN ⇲ Free Frieda logs in with her valid credentials + THEN ⇲ she sees a Free article + +SCENARIO ⇲ [🔴]Subscriber with a paid subscription can access both free and paid articles + GIVEN ⇲ Paid Patty has a basic-level paid subscription + WHEN ⇲ Paid Patty logs in with her valid credentials + THEN ⇲ she sees a Free article and a Paid article ``` ### 🔎 Is GherXunit for You? diff --git a/README_PTBR.md b/README_PTBR.md index 665ec37..af3c7e7 100644 --- a/README_PTBR.md +++ b/README_PTBR.md @@ -89,17 +89,21 @@ public partial class SubscriptionTest(ITestOutputHelper output): IGherXunit #### 📌 Exemplo de saída destacando os resultados dos testes: O resultado da execução dos cenários de teste definidos na classe `SubscriptionTest` seria semelhante à saída a seguir: -```gherkindotnet -✅ Dr. Bill posts to his own blog -GIVEN a global administrator named <<"Greg">> - AND a blog named <<"Greg's anti-tax rants">> - AND a customer named <<"Dr. Bill">> - AND a blog named <<"Expensive Therapy">> owned by <<"Dr. Bill">> - -✅ Dr. Bill posts to his own blog -GIVEN I am logged in as Dr. Bill - WHEN I try to post to "Expensive Therapy" - THEN I should see "Your article was published." +```shell +11:11:49.683 |V| Run: 0821bae4-1a1a-447b-807e-2eb5042f1fe5 - Discovery result processing started +11:11:49.688 |V| Run: 0821bae4-1a1a-447b-807e-2eb5042f1fe5 - Discovery result processing finished: (+0 ~2 -0), interrupted: 0 +11:11:49.879 |I| Process /usr/local/share/dotnet/x64/dotnet:21042 has exited with code (0) +11:11:49.879 |I| Output stream: + +SCENARIO ⇲ [🟢]Free subscribers see only the free articles + GIVEN ⇲ Free Frieda has a free subscription + WHEN ⇲ Free Frieda logs in with her valid credentials + THEN ⇲ she sees a Free article + +SCENARIO ⇲ [🔴]Subscriber with a paid subscription can access both free and paid articles + GIVEN ⇲ Paid Patty has a basic-level paid subscription + WHEN ⇲ Paid Patty logs in with her valid credentials + THEN ⇲ she sees a Free article and a Paid article ``` ### 🔎 O GherXunit é para você? diff --git a/src/GherXunit.sln b/src/GherXunit.sln index 53e1362..7e2d696 100644 --- a/src/GherXunit.sln +++ b/src/GherXunit.sln @@ -10,7 +10,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BddSample", "sample\BddSamp EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "base", "base", "{B014B043-DFB5-4827-8775-D4E5326130F0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GherXunitCore", "base\GherXunitCore\GherXunitCore.csproj", "{5477E886-B647-4A5D-A522-2978AF9F748D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GherXunit.Core", "base\GherXunit.Core\GherXunit.Core.csproj", "{5477E886-B647-4A5D-A522-2978AF9F748D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/base/GherXunitCore/AttributeSourceCode.cs b/src/base/GherXunit.Core/Attributes.cs similarity index 69% rename from src/base/GherXunitCore/AttributeSourceCode.cs rename to src/base/GherXunit.Core/Attributes.cs index 50b8a60..3d11a2f 100644 --- a/src/base/GherXunitCore/AttributeSourceCode.cs +++ b/src/base/GherXunit.Core/Attributes.cs @@ -5,21 +5,24 @@ namespace GherXunit.Annotations; +// Description attributes public sealed class FeatureAttribute(string description) : DescriptionAttribute($"Feature: {description}"); public sealed class RuleAttribute(string description) : DescriptionAttribute($"Rule: {description}"); public sealed class BackgroundAttribute() : DescriptionAttribute("Background"); -public sealed class ScenarioAttribute(string displayName) : GherXunitCustomFactAttribute(displayName); -public sealed class ExampleAttribute(string displayName) : GherXunitCustomFactAttribute(displayName); -public sealed class ScenarioOutlineAttribute(string displayName) : GherXunitCustomTheoryAttribute(displayName); -public sealed class ScenariosAttribute(string displayName) : GherXunitCustomTheoryAttribute(displayName); +// Xunit attributes +public sealed class ScenarioAttribute(string displayName) : GherXunitFactAttribute(displayName); +public sealed class ExampleAttribute(string displayName) : GherXunitFactAttribute(displayName); +public sealed class ScenarioOutlineAttribute(string displayName) : GherXunitTheoryAttribute(displayName); +public sealed class ScenariosAttribute(string displayName) : GherXunitTheoryAttribute(displayName); -public class GherXunitCustomFactAttribute(string displayName) : FactAttribute +// Custom Xunit attributes +public class GherXunitFactAttribute(string displayName) : FactAttribute { public override string DisplayName { get; set; } = displayName; } -public class GherXunitCustomTheoryAttribute(string displayName) : TheoryAttribute +public class GherXunitTheoryAttribute(string displayName) : TheoryAttribute { public override string DisplayName { get; set; } = displayName; } diff --git a/src/base/GherXunit.Core/Features/SubscriptionTest.Steps.cs b/src/base/GherXunit.Core/Features/SubscriptionTest.Steps.cs new file mode 100644 index 0000000..147f7d1 --- /dev/null +++ b/src/base/GherXunit.Core/Features/SubscriptionTest.Steps.cs @@ -0,0 +1,11 @@ +using GherXunit.Annotations; +using Xunit.Abstractions; + +namespace BddTests.Samples.Features; + +public partial class SubscriptionTest(ITestOutputHelper output): IGherXunit +{ + public ITestOutputHelper Output { get; } = output; + private void WhenPattyLogsSteps() => Assert.True(true); + private async Task WhenFriedaLogsSteps() => await Task.CompletedTask; +} \ No newline at end of file diff --git a/src/base/GherXunit.Core/Features/SubscriptionTest.cs b/src/base/GherXunit.Core/Features/SubscriptionTest.cs new file mode 100644 index 0000000..452cc1a --- /dev/null +++ b/src/base/GherXunit.Core/Features/SubscriptionTest.cs @@ -0,0 +1,25 @@ +using GherXunit.Annotations; + +namespace BddTests.Samples.Features; + +[Feature("Subscribers see different articles based on their subscription level")] +public partial class SubscriptionTest +{ + [Scenario("Free subscribers see only the free articles")] + async Task WhenFriedaLogs() => await this.ExecuteAscync( + refer: WhenFriedaLogsSteps, + steps: """ + Given Free Frieda has a free subscription + When Free Frieda logs in with her valid credentials + Then she sees a Free article + """); + + [Scenario("Subscriber with a paid subscription can access both free and paid articles")] + void WhenPattyLogs() => this.Execute( + refer: WhenPattyLogsSteps, + steps: """ + Given Paid Patty has a basic-level paid subscription + When Paid Patty logs in with her valid credentials + Then she sees a Free article and a Paid article + """); +} \ No newline at end of file diff --git a/src/base/GherXunitCore/GherXunitCore.csproj b/src/base/GherXunit.Core/GherXunit.Core.csproj similarity index 100% rename from src/base/GherXunitCore/GherXunitCore.csproj rename to src/base/GherXunit.Core/GherXunit.Core.csproj diff --git a/src/base/GherXunitCore/InterfaceSourceCode.cs b/src/base/GherXunit.Core/Interfaces.cs similarity index 90% rename from src/base/GherXunitCore/InterfaceSourceCode.cs rename to src/base/GherXunit.Core/Interfaces.cs index 15f2454..916e7fd 100644 --- a/src/base/GherXunitCore/InterfaceSourceCode.cs +++ b/src/base/GherXunit.Core/Interfaces.cs @@ -4,16 +4,16 @@ namespace GherXunit.Annotations; +// Obsolete attributes [Obsolete($"Use {nameof(IGherXunit)} instead")] public interface IGherXunitBackground : IGherXunitStep, IClassFixture where T : class; +// Interfaces +public interface IGherXunitStep; public interface IGherXunit : IGherXunitStep, IGherXunitOutputProvider; - public interface IGherXunit : IGherXunit, IClassFixture where T : class; -public interface IGherXunitStep; - - +// Output provider public interface IGherXunitOutputProvider { public ITestOutputHelper Output { get; } diff --git a/src/base/GherXunitCore/StepSourceCode.cs b/src/base/GherXunit.Core/Methods.cs similarity index 59% rename from src/base/GherXunitCore/StepSourceCode.cs rename to src/base/GherXunit.Core/Methods.cs index 4d75d03..ccc613f 100644 --- a/src/base/GherXunitCore/StepSourceCode.cs +++ b/src/base/GherXunit.Core/Methods.cs @@ -7,17 +7,20 @@ namespace GherXunit.Annotations; public static class GherXunitSteps { + // Async methods public static async Task ExecuteAscync(this IGherXunitStep feature, string steps) => await ExecuteAscync(feature, null, steps, []); public static async Task ExecuteAscync(this IGherXunitStep feature, Delegate refer, string steps) => await ExecuteAscync(feature, refer.Method, steps); public static async Task ExecuteAscync(this IGherXunitStep feature, Delegate refer, object[] param, string steps) => await ExecuteAscync(feature, refer.Method, steps, param); - public static async Task NonExecutableAsync(this IGherXunitStep feature, string? steps = null) => await feature.WriteAsync(null, steps); + public static async Task NonExecutableAsync(this IGherXunitStep feature, string? steps = null) => await ExecuteAscync(feature, null, steps, []); + // Sync methods public static void Execute(this IGherXunitStep feature, string steps) => Execute(feature, null, steps, []); public static void Execute(this IGherXunitStep feature, Delegate refer, string steps) => Execute(feature, refer.Method, steps); public static void Execute(this IGherXunitStep feature, Delegate refer, object[] param, string steps) => Execute(feature, refer.Method, steps, param); - public static void NonExecutable(this IGherXunitStep feature, string? steps) => feature.Write(null, steps); + public static void NonExecutable(this IGherXunitStep feature, string? steps) => Execute(feature, null, steps, []); - private static void Execute(this IGherXunitStep feature, MethodInfo? method, string steps, params object?[] param) + // Private methods + private static void Execute(this IGherXunitStep feature, MethodInfo? method, string? steps, params object?[] param) { try { @@ -31,61 +34,53 @@ private static void Execute(this IGherXunitStep feature, MethodInfo? method, str } } - private static async Task ExecuteAscync(this IGherXunitStep feature, MethodInfo? method, string steps, params object?[] param) + private static async Task ExecuteAscync(this IGherXunitStep feature, MethodInfo? method, string? steps, + params object?[] param) { try { var task = method is null ? Task.CompletedTask : (Task)method.Invoke(feature, param)!; await task; - await feature.WriteAsync(method?.Name, steps); + feature.Write(method?.Name, steps); } catch (Exception) { - await feature.WriteAsync(method?.Name, steps, true); + feature.Write(method?.Name, steps, true); throw; } } - private static async Task WriteAsync(this IGherXunitStep feature, string? methodName, string? steps, bool isException = false) - { - if (steps is null) return; - Write(feature, methodName, steps, isException); - await Task.Yield(); - } - private static void Write(this IGherXunitStep feature, string? methodName, string? steps, bool isException = false) { - var status = isException ? "\u274c" : "\u2705"; - if (steps is null) return; - var stepString = new StepStringHandler(); - stepString.AppendLiteral(steps); - var displayName = methodName is null ? status : $"{status} {methodName}"; - if (feature is IGherXunit { Output: { } output }) - { - var iTest = GetTest(output); - displayName = iTest is null ? status : $"{status} {iTest.DisplayName}"; + var status = isException ? "🔴" : "🟢"; + var iTest = GetTest(feature, out var output); - output.WriteLine(displayName); - output.WriteLine(stepString.ToString()); - output.WriteLine(string.Empty); - } + var display = iTest is null + ? $"Scenario [{status}]{methodName}\r\n{steps}" + : $"Scenario [{status}]{iTest.DisplayName}\r\n{steps}"; - Console.WriteLine(displayName); - Console.WriteLine(steps); + var stepString = new StepStringHandler(); + stepString.AppendLiteral(display); + + output?.WriteLine(string.Empty); + output?.WriteLine(stepString.ToString()); Console.WriteLine(string.Empty); + Console.WriteLine(stepString.ToString()); } - public static ITest? GetTest(ITestOutputHelper outputHelper) + private static ITest? GetTest(IGherXunitStep feature, out ITestOutputHelper? outputHelper) { - if (outputHelper is not TestOutputHelper testOutputHelper) return null; - - var testField = - typeof(TestOutputHelper).GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) ?? + outputHelper = null; + if (feature is not IGherXunit { Output: { } output }) return null; + + var testField = + typeof(TestOutputHelper).GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(TestOutputHelper).GetField("_test", BindingFlags.Instance | BindingFlags.NonPublic); - return testField?.GetValue(testOutputHelper) as ITest; + outputHelper = output; + return testField?.GetValue(output) as ITest; } } \ No newline at end of file diff --git a/src/base/GherXunitCore/StepStringHandler.cs b/src/base/GherXunit.Core/StepStringHandler.cs similarity index 62% rename from src/base/GherXunitCore/StepStringHandler.cs rename to src/base/GherXunit.Core/StepStringHandler.cs index 51b6a2d..907806f 100644 --- a/src/base/GherXunitCore/StepStringHandler.cs +++ b/src/base/GherXunit.Core/StepStringHandler.cs @@ -15,9 +15,10 @@ public ref struct StepStringHandler() private string HighlightKeyword(string input) { return input - .Replace("Given", "GIVEN".PadLeft(5)) // Verde - .Replace("When", "WHEN".PadLeft(5)) // Azul - .Replace("Then", "THEN".PadLeft(5)) // Amarelo - .Replace("And", "AND".PadLeft(5)); // Ciano + .Replace("Scenario", $"{"SCENARIO",8} ⇲") // Verde + .Replace("Given", $"{"GIVEN",8} ⇲") // Verde + .Replace("When", $"{"WHEN",8} ⇲") // Azul + .Replace("Then", $"{"THEN",8} ⇲") // Amarelo + .Replace("And", $"{"AND",8} ⇲"); // Ciano } } \ No newline at end of file diff --git a/src/lib/GherXunit/GherXUnitSourceGenerator.cs b/src/lib/GherXunit/GherXUnit.Source.Generator.cs similarity index 81% rename from src/lib/GherXunit/GherXUnitSourceGenerator.cs rename to src/lib/GherXunit/GherXUnit.Source.Generator.cs index f2fb1aa..17bf2c8 100644 --- a/src/lib/GherXunit/GherXUnitSourceGenerator.cs +++ b/src/lib/GherXunit/GherXUnit.Source.Generator.cs @@ -18,8 +18,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) spc.AddSource("GherXunitStepStringHandler.g.cs", SourceText.From(GherXunitStepStringHandler.SOURCE, Encoding.UTF8)); spc.AddSource("GherXUnitInterfaces.g.cs", SourceText.From(GherXunitInterfaces.SOURCE, Encoding.UTF8)); - spc.AddSource("GherXUnitSteps.g.cs", SourceText.From(GherXunitExecutedSteps.SOURCE, Encoding.UTF8)); - spc.AddSource("GherXUnitAnnotations.g.cs", SourceText.From(GherXunitAttributes.SOURCE, Encoding.UTF8)); + spc.AddSource("GherXunitMethods.g.cs", SourceText.From(GherXunitMethods.SOURCE, Encoding.UTF8)); + spc.AddSource("GherXUnitAttributes.g.cs", SourceText.From(GherXunitAttributes.SOURCE, Encoding.UTF8)); }); } } \ No newline at end of file diff --git a/src/lib/GherXunit/GherXunitAttributes.cs b/src/lib/GherXunit/GherXunit.Content.Attributes.cs similarity index 75% rename from src/lib/GherXunit/GherXunitAttributes.cs rename to src/lib/GherXunit/GherXunit.Content.Attributes.cs index 4c93f69..246326f 100644 --- a/src/lib/GherXunit/GherXunitAttributes.cs +++ b/src/lib/GherXunit/GherXunit.Content.Attributes.cs @@ -16,20 +16,24 @@ public struct GherXunitAttributes namespace GherXunit.Annotations; + // Description attributes public sealed class FeatureAttribute(string description) : DescriptionAttribute($"Feature: {description}"); public sealed class RuleAttribute(string description) : DescriptionAttribute($"Rule: {description}"); public sealed class BackgroundAttribute() : DescriptionAttribute("Background"); - public sealed class ScenarioAttribute(string displayName) : GherXunitCustomFactAttribute(displayName); - public sealed class ExampleAttribute(string displayName) : GherXunitCustomFactAttribute(displayName); - public sealed class ScenarioOutlineAttribute(string displayName) : GherXunitCustomTheoryAttribute(displayName); - public sealed class ScenariosAttribute(string displayName) : GherXunitCustomTheoryAttribute(displayName); - public class GherXunitCustomFactAttribute(string displayName) : FactAttribute + // Xunit attributes + public sealed class ScenarioAttribute(string displayName) : GherXunitFactAttribute(displayName); + public sealed class ExampleAttribute(string displayName) : GherXunitFactAttribute(displayName); + public sealed class ScenarioOutlineAttribute(string displayName) : GherXunitTheoryAttribute(displayName); + public sealed class ScenariosAttribute(string displayName) : GherXunitTheoryAttribute(displayName); + + // Custom Xunit attributes + public class GherXunitFactAttribute(string displayName) : FactAttribute { public override string DisplayName { get; set; } = displayName; } - public class GherXunitCustomTheoryAttribute(string displayName) : TheoryAttribute + public class GherXunitTheoryAttribute(string displayName) : TheoryAttribute { public override string DisplayName { get; set; } = displayName; } diff --git a/src/lib/GherXunit/GherXunitInterfaces.cs b/src/lib/GherXunit/GherXunit.Content.Interfaces.cs similarity index 91% rename from src/lib/GherXunit/GherXunitInterfaces.cs rename to src/lib/GherXunit/GherXunit.Content.Interfaces.cs index 339c1aa..068568f 100644 --- a/src/lib/GherXunit/GherXunitInterfaces.cs +++ b/src/lib/GherXunit/GherXunit.Content.Interfaces.cs @@ -14,15 +14,16 @@ public struct GherXunitInterfaces namespace GherXunit.Annotations; + // Obsolete attributes [Obsolete($"Use {nameof(IGherXunit)} instead")] public interface IGherXunitBackground : IGherXunitStep, IClassFixture where T : class; + // Interfaces + public interface IGherXunitStep; public interface IGherXunit : IGherXunitStep, IGherXunitOutputProvider; - public interface IGherXunit : IGherXunit, IClassFixture where T : class; - public interface IGherXunitStep; - + // Output provider public interface IGherXunitOutputProvider { public ITestOutputHelper Output { get; } diff --git a/src/lib/GherXunit/GherXunitExecutedSteps.cs b/src/lib/GherXunit/GherXunit.Content.Methods.cs similarity index 62% rename from src/lib/GherXunit/GherXunitExecutedSteps.cs rename to src/lib/GherXunit/GherXunit.Content.Methods.cs index d3dd38b..3a7af33 100644 --- a/src/lib/GherXunit/GherXunitExecutedSteps.cs +++ b/src/lib/GherXunit/GherXunit.Content.Methods.cs @@ -4,7 +4,7 @@ namespace GherXunit.Annotations; /// The source code of the methods that are used to execute the Gherkin steps. /// This works as a superset of the Xunit methods and it is used to create the custom methods /// -public struct GherXunitExecutedSteps +public struct GherXunitMethods { internal const string SOURCE = """ @@ -12,22 +12,25 @@ public struct GherXunitExecutedSteps using System.Reflection; using Xunit.Abstractions; using Xunit.Sdk; - + namespace GherXunit.Annotations; - + public static class GherXunitSteps { + // Async methods public static async Task ExecuteAscync(this IGherXunitStep feature, string steps) => await ExecuteAscync(feature, null, steps, []); public static async Task ExecuteAscync(this IGherXunitStep feature, Delegate refer, string steps) => await ExecuteAscync(feature, refer.Method, steps); public static async Task ExecuteAscync(this IGherXunitStep feature, Delegate refer, object[] param, string steps) => await ExecuteAscync(feature, refer.Method, steps, param); - public static async Task NonExecutableAsync(this IGherXunitStep feature, string? steps = null) => await feature.WriteAsync(null, steps); + public static async Task NonExecutableAsync(this IGherXunitStep feature, string? steps = null) => await ExecuteAscync(feature, null, steps, []); + // Sync methods public static void Execute(this IGherXunitStep feature, string steps) => Execute(feature, null, steps, []); public static void Execute(this IGherXunitStep feature, Delegate refer, string steps) => Execute(feature, refer.Method, steps); public static void Execute(this IGherXunitStep feature, Delegate refer, object[] param, string steps) => Execute(feature, refer.Method, steps, param); - public static void NonExecutable(this IGherXunitStep feature, string? steps) => feature.Write(null, steps); + public static void NonExecutable(this IGherXunitStep feature, string? steps) => Execute(feature, null, steps, []); - private static void Execute(this IGherXunitStep feature, MethodInfo? method, string steps, params object?[] param) + // Private methods + private static void Execute(this IGherXunitStep feature, MethodInfo? method, string? steps, params object?[] param) { try { @@ -41,62 +44,54 @@ private static void Execute(this IGherXunitStep feature, MethodInfo? method, str } } - private static async Task ExecuteAscync(this IGherXunitStep feature, MethodInfo? method, string steps, params object?[] param) + private static async Task ExecuteAscync(this IGherXunitStep feature, MethodInfo? method, string? steps, + params object?[] param) { try { var task = method is null ? Task.CompletedTask : (Task)method.Invoke(feature, param)!; await task; - await feature.WriteAsync(method?.Name, steps); + feature.Write(method?.Name, steps); } catch (Exception) { - await feature.WriteAsync(method?.Name, steps, true); + feature.Write(method?.Name, steps, true); throw; } } - private static async Task WriteAsync(this IGherXunitStep feature, string? methodName, string? steps, bool isException = false) - { - if (steps is null) return; - Write(feature, methodName, steps, isException); - await Task.Yield(); - } - private static void Write(this IGherXunitStep feature, string? methodName, string? steps, bool isException = false) { - var status = isException ? "\u274c" : "\u2705"; - if (steps is null) return; - var stepString = new StepStringHandler(); - stepString.AppendLiteral(steps); - var displayName = methodName is null ? status : $"{status} {methodName}"; - if (feature is IGherXunit { Output: { } output }) - { - var iTest = GetTest(output); - displayName = iTest is null ? status : $"{status} {iTest.DisplayName}"; + var status = isException ? "🔴" : "🟢"; + var iTest = GetTest(feature, out var output); - output.WriteLine(displayName); - output.WriteLine(stepString.ToString()); - output.WriteLine(string.Empty); - } + var display = iTest is null + ? $"Scenario [{status}]{methodName}\r\n{steps}" + : $"Scenario [{status}]{iTest.DisplayName}\r\n{steps}"; + + var stepString = new StepStringHandler(); + stepString.AppendLiteral(display); - Console.WriteLine(displayName); - Console.WriteLine(steps); + output?.WriteLine(string.Empty); + output?.WriteLine(stepString.ToString()); Console.WriteLine(string.Empty); + Console.WriteLine(stepString.ToString()); } - public static ITest? GetTest(ITestOutputHelper outputHelper) + private static ITest? GetTest(IGherXunitStep feature, out ITestOutputHelper? outputHelper) { - if (outputHelper is not TestOutputHelper testOutputHelper) return null; - - var testField = - typeof(TestOutputHelper).GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) ?? + outputHelper = null; + if (feature is not IGherXunit { Output: { } output }) return null; + + var testField = + typeof(TestOutputHelper).GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) ?? typeof(TestOutputHelper).GetField("_test", BindingFlags.Instance | BindingFlags.NonPublic); - return testField?.GetValue(testOutputHelper) as ITest; + outputHelper = output; + return testField?.GetValue(output) as ITest; } } """; diff --git a/src/lib/GherXunit/GherXunitStepStringHandler.cs b/src/lib/GherXunit/GherXunit.Content.StringHandler.cs similarity index 70% rename from src/lib/GherXunit/GherXunitStepStringHandler.cs rename to src/lib/GherXunit/GherXunit.Content.StringHandler.cs index ba64daa..1eca5fc 100644 --- a/src/lib/GherXunit/GherXunitStepStringHandler.cs +++ b/src/lib/GherXunit/GherXunit.Content.StringHandler.cs @@ -21,10 +21,11 @@ public ref struct StepStringHandler() private string HighlightKeyword(string input) { return input - .Replace("Given", "GIVEN".PadLeft(5)) // Verde - .Replace("When", "WHEN".PadLeft(5)) // Azul - .Replace("Then", "THEN".PadLeft(5)) // Amarelo - .Replace("And", "AND".PadLeft(5)); // Ciano + .Replace("Scenario", $"{"SCENARIO",8} ⇲") // Verde + .Replace("Given", $"{"GIVEN",8} ⇲") // Verde + .Replace("When", $"{"WHEN",8} ⇲") // Azul + .Replace("Then", $"{"THEN",8} ⇲") // Amarelo + .Replace("And", $"{"AND",8} ⇲"); // Ciano } } """; diff --git a/src/lib/GherXunit/GherXunit.csproj b/src/lib/GherXunit/GherXunit.csproj index 24e7c1d..3caf059 100644 --- a/src/lib/GherXunit/GherXunit.csproj +++ b/src/lib/GherXunit/GherXunit.csproj @@ -7,7 +7,7 @@ true true GherXunit - 1.0.0 + 1.1.0 Yan Justino Emergingcode GherXunit emerges as a viable alternative for those seeking an approach that combines the expressiveness of BDD with the well-established structure of xUnit. diff --git a/src/sample/BddSample/Samples/Features/SubscriptionTest.Steps.cs b/src/sample/BddSample/Samples/Features/SubscriptionTest.Steps.cs index e50fcae..147f7d1 100644 --- a/src/sample/BddSample/Samples/Features/SubscriptionTest.Steps.cs +++ b/src/sample/BddSample/Samples/Features/SubscriptionTest.Steps.cs @@ -6,6 +6,6 @@ namespace BddTests.Samples.Features; public partial class SubscriptionTest(ITestOutputHelper output): IGherXunit { public ITestOutputHelper Output { get; } = output; - private void WhenPattyLogsSteps() { } + private void WhenPattyLogsSteps() => Assert.True(true); private async Task WhenFriedaLogsSteps() => await Task.CompletedTask; } \ No newline at end of file