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
42 changes: 2 additions & 40 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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/**/*.
Expand Down
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
```
Expand All @@ -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?
Expand Down
26 changes: 15 additions & 11 deletions README_PTBR.md
Original file line number Diff line number Diff line change
Expand Up @@ -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ê?
Expand Down
2 changes: 1 addition & 1 deletion src/GherXunit.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
11 changes: 11 additions & 0 deletions src/base/GherXunit.Core/Features/SubscriptionTest.Steps.cs
Original file line number Diff line number Diff line change
@@ -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;
}
25 changes: 25 additions & 0 deletions src/base/GherXunit.Core/Features/SubscriptionTest.cs
Original file line number Diff line number Diff line change
@@ -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
""");
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace GherXunit.Annotations;

// Obsolete attributes
[Obsolete($"Use {nameof(IGherXunit<T>)} instead")]
public interface IGherXunitBackground<T> : IGherXunitStep, IClassFixture<T> where T : class;

// Interfaces
public interface IGherXunitStep;
public interface IGherXunit : IGherXunitStep, IGherXunitOutputProvider;

public interface IGherXunit<T> : IGherXunit, IClassFixture<T> where T : class;

public interface IGherXunitStep;


// Output provider
public interface IGherXunitOutputProvider
{
public ITestOutputHelper Output { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading