diff --git a/.github/labeler.yml b/.github/labeler.yml index 57313b26..03e39fe0 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -31,6 +31,6 @@ "theme:specflow": - changed-files: - any-glob-to-any-file: - - "Allure.Features/**" - - "Allure.SpecFlowPlugin/**" - - "Allure.SpecFlowPlugin.Tests/**" + - "Allure.SpecFlow/**" + - "Allure.SpecFlow.Tests/**" + - "Allure.SpecFlow.Tests.Samples/**" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a2da6737..d9095975 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,6 +44,6 @@ jobs: dotnet test Allure.Net.Commons.Tests/Allure.Net.Commons.Tests.csproj\ --no-build\ --configuration ${{ env.BUILD_CONFIGURATION }} - dotnet test Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj\ + dotnet test Allure.SpecFlow.Tests/Allure.SpecFlow.Tests.csproj\ --no-build\ --configuration ${{ env.BUILD_CONFIGURATION }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ce46efb..a146eb96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,20 +53,20 @@ jobs: cat ./Allure.NUnit/Schemas/allureConfig.schema.json sed -i -e '/"$ref"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.Xunit/Schemas/allureConfig.schema.json cat ./Allure.Xunit/Schemas/allureConfig.schema.json - sed -i -e '/"$ref"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json - cat ./Allure.SpecFlowPlugin/Schemas/allureConfig.schema.json + sed -i -e '/"$ref"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlow/Schemas/allureConfig.schema.json + cat ./Allure.SpecFlow/Schemas/allureConfig.schema.json sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.Net.Commons/allureConfig.Template.json cat ./Allure.Net.Commons/allureConfig.Template.json sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.NUnit.Examples/allureConfig.json cat ./Allure.NUnit.Examples/allureConfig.json sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.Xunit.Examples/allureConfig.json cat ./Allure.Xunit.Examples/allureConfig.json - sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlowPlugin/allureConfig.Template.json - cat ./Allure.SpecFlowPlugin/allureConfig.Template.json - sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.Features/allureConfig.json - cat ./Allure.Features/allureConfig.json - sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlowPlugin.Tests/allureConfig.json - cat ./Allure.SpecFlowPlugin.Tests/allureConfig.json + sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlow/allureConfig.Template.json + cat ./Allure.SpecFlow/allureConfig.Template.json + sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlow.Tests.Samples/allureConfig.json + cat ./Allure.SpecFlow.Tests.Samples/allureConfig.json + sed -i -e '/"$schema"/s|/allure-csharp/[^/]*|/allure-csharp/${{ env.release_version }}|g' ./Allure.SpecFlow.Tests/allureConfig.json + cat ./Allure.SpecFlow.Tests/allureConfig.json - name: "Update release notes link" run: | sed -i -e '//,/<\/PackageReleaseNotes>/ s|/allure-csharp/releases/tag/.*\.|/allure-csharp/releases/tag/${{ inputs.nextVersion }}.|g' ./Directory.Build.props diff --git a/Allure.Features/Steps.cs b/Allure.Features/Steps.cs deleted file mode 100644 index e3f1d7cd..00000000 --- a/Allure.Features/Steps.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading; -using Allure.Net.Commons; -using Allure.SpecFlowPlugin; -using TechTalk.SpecFlow; - -namespace Allure.Features -{ - public enum TestOutcome - { - passed, - failed - } - - [Binding] - public class Steps - { - private static readonly AllureLifecycle allure = AllureLifecycle.Instance; - - private FeatureContext featureContext; - private readonly ScenarioContext scenarioContext; - - public Steps(FeatureContext featureContext, ScenarioContext scenarioContext) - { - this.featureContext = featureContext; - this.scenarioContext = scenarioContext; - } - - [StepDefinition(@"Step is '(.*)'")] - public void StepResultIs(TestOutcome outcome) - { - switch (outcome) - { - case TestOutcome.passed: - break; - case TestOutcome.failed: - throw new Exception("This test is failed", - new InvalidOperationException("Internal message", - new ArgumentException("One more message"))); - default: - throw new ArgumentException("value is not supported"); - } - } - - [StepDefinition("Step with attachment")] - public void StepWithAttach() - { - var path = Guid.NewGuid().ToString(); - File.WriteAllText(path, "hi there"); - AllureApi.AddAttachment(path); - } - - [StepDefinition("Step with table")] - public void StepWithTable(Table table) - { - } - - [StepDefinition("Step with params: (.*)")] - public void StepWithArgs(int number, string text) - { - } - - [BeforeTestRun] - public static void BeforeTestRun() - { - AllureLifecycle.Instance.CleanupResultDirectory(); - } - [BeforeFeature("beforefeaturepassed", Order = 1)] - public static void PassedBeforeFeature() - { - } - - [AfterFeature("afterfeaturepassed", Order = 1)] - public static void PassedAfterFeature() - { - } - - [BeforeFeature("beforefeaturefailed")] - public static void FailedBeforeFeature() - { - throw new Exception("Failed Before Feature"); - } - - [AfterFeature("afterfeaturefailed")] - public static void FailedAfterFeature() - { - throw new Exception("Failed After Feature"); - } - - [BeforeScenario(tags: "beforescenario")] - [AfterScenario(tags: "afterscenario")] - [BeforeStep(tags: "beforestep")] - [AfterStep(tags: "afterstep")] - public void HandleIt() - { - Handle(scenarioContext.ScenarioInfo.Tags); - } - - private static void Handle(string[] tags) - { - if (tags != null && tags.Contains("attachment")) - { - var path = $"{Guid.NewGuid().ToString()}.txt"; - File.WriteAllText(path, "hi there"); - AllureApi.AddAttachment(path); - AllureApi.AddAttachment(path, "text file"); - } - - if (tags != null) - if (tags.Any(x => x.EndsWith(Status.failed.ToString()) || x.EndsWith(Status.broken.ToString()))) - throw new Exception("Wasted"); - else if (tags.Any(x => x == PluginHelper.IGNORE_EXCEPTION)) - throw new IgnoreException(); - } - } -} \ No newline at end of file diff --git a/Allure.NUnit/README.md b/Allure.NUnit/README.md index 118b3db2..88ce2076 100644 --- a/Allure.NUnit/README.md +++ b/Allure.NUnit/README.md @@ -21,10 +21,10 @@ ## Quick start - Install the Allure.NUnit package. -- Configure allureConfig.json. -- Apply the `[AllureNUnit]` attribute to test fixtures. -- Use other attributes in `NUnit.Allure.Attributes` if needed. -- Use the functions in `Allure.Net.Commons.AllureApi` if needed. +- Configure it via allureConfig.json. +- Apply the `[Allure.NUnit.AllureNUnit]` attribute to test fixtures. +- Use other attributes from `Allure.NUnit.Attributes` if needed. +- Use the functions from `Allure.Net.Commons.AllureApi` if needed. ## Further readings @@ -34,13 +34,66 @@ Some examples are available [here](https://github.com/allure-framework/allure-cs ## Notes -### NUnit.Allure.Core.StepsHelper deprecation +### Namespace changed to Allure.NUnit -The new `Allure.Net.Commons.AllureApi` facade class was designed specificially -for test authors to enhance the Allure report. Prefer using functions in this -class over the ones from `NUnit.Allure.Core.StepsHelper`. +Starting from 2.12.0, the namespace `NUnit.Allure` is deprecated. The API in +that namespace still works, but it will be removed in the future. Please use +`Allure.NUnit` instead. + +> The `[NUnit.Allure.Core.AllureNUnit]` attribute should be replaced with +> `[Allure.NUnit.AllureNUnit]`: + +```c# +using Allure.NUnit; // <- Note the namespace +using NUnit.Framework; + +[AllureNUnit] +class MyTests +{ + [Test] + public void TestMethod() + { + /* ... */ + } +} +``` + +### Deprecations and removals in 2.12.0 + +The following user API methods are now deprecated: + + - In `NUnit.Allure.Core.AllureExtensions`: + - All overloads of `WrapInStep` - use `Allure.Net.Commons.AllureApi.Step` + instead. + - `WrapSetUpTearDownParams` - had no effect; can safely be replaced with + the direct call of the provided delegate. + - `NUnit.Allure.Core.AllureNUnitAttribute` - use + `Allure.NUnit.AllureNUnitAttribute` instead. + - Other classes and methods in `NUnit.Allure` - change the namespace to + `Allure.NUnit`. + +The following previously deprecated user API classes and methods were removed: + + - In `NUnit.Allure.Core.AllureExtensions`: + - `AddScreenDiff` - use `Allure.Net.Commons.AllureApi.AddScreenDiff` + instead. + - `NUnit.Allure.Core.AllureNUnitAttribute`'s constructor overload that takes + `bool wrapIntoStep` - the `wrapIntoStep` parameter had no effect and can be + safely removed now. + - In `NUnit.Allure.Core.AllureNUnitHelper`: + - `WrapInStep` - use `Allure.Net.Commons.AllureApi.Step` instead. + - `NUnit.Allure.Core.StepsHelper` - use functions from + `Allure.Net.Commons.AllureApi` and `Allure.Net.Commons.ExtendedApi` instead. + - In `Allure.Net.Commons.AllureLifecycle`: + - `AddAttachment` - use `Allure.Net.Commons.AllureApi.AddAttachment` + instead. + - `AddScreenDiff` - use `Allure.Net.Commons.AllureApi.AddScreenDiff` + instead. + - `Allure.Net.Commons.Steps.CoreStepsHelper` - use functions from + `Allure.Net.Commons.AllureApi` and `Allure.Net.Commons.ExtendedApi` instead. ### For users of Mac with Apple silicon + If you're developing on a Mac machine with Apple silicon, make sure you have Rosetta installed. Follow this article for the instructions: https://support.apple.com/en-us/HT211861 diff --git a/Allure.Net.Commons/README.md b/Allure.Net.Commons/README.md index 036822af..0f18c998 100644 --- a/Allure.Net.Commons/README.md +++ b/Allure.Net.Commons/README.md @@ -1,4 +1,5 @@ # Allure.Net.Commons + [![](http://img.shields.io/nuget/vpre/Allure.Net.Commons.svg?style=flat)](https://www.nuget.org/packages/Allure.Net.Commons) .Net implementation of [Allure java-commons](https://github.com/allure-framework/allure-java/tree/master/allure-java-commons). @@ -15,9 +16,9 @@ --- -The library can be used by any project targeting a netstandard2.0 compatible -framework including .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5.0+ and more. -See the full list [here](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0#select-net-standard-version). +The library can be used by any project that targets a framework compatible with +.NET Standard 2.0 (.NET Framework 4.6.1+, .NET Core 2.0+, .NET 5.0+, and more). +See the complete list [here](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0#select-net-standard-version). ## Note for users of Mac with Apple silicon @@ -32,48 +33,75 @@ You may also install Rosetta via the CLI: ``` ## Configuration -Allure lifecycle is configured via a json file with the default name `allureConfig.json`. NuGet package installs `allureConfig.Template.json` which you can use as an example. There are 2 ways to specify config file location: -- set ALLURE_CONFIG environment variable to the full path of json config file. This option is preferable for .net core projects which utilize nuget libraries directly from nuget packages folder. See this example of setting it in the code: https://github.com/allure-framework/allure-csharp/blob/bdf11bd3e1f41fd1e4a8fd22fa465b90b68e9d3f/Allure.Commons.NetCore.Tests/AllureConfigTests.cs#L13-L15 -- place `allureConfig.json` to the location of `Allure.Net.Commons.dll`. This option can be used with .net classic projects which copy all referenced package libraries into binary folder. Do not forget to set 'Copy to Output Directory' property to 'Copy always' or 'Copy if newer' in your test project or set it in .csproj: - ```xml - - - PreserveNewest - - - ``` +The Allure lifecycle is configured via a JSON file with the default name +`allureConfig.json`. NuGet package installs `allureConfig.Template.json`, which +you can use as an example. There are two ways to specify config file location: -Allure lifecycle will start with default configuration settings if `allureConfig.json` is not found. + - Set the ALLURE_CONFIG environment variable to the full path of the file. + - Add `allureConfig.json` to the project and ensure it's copied to the + project output directory next to `Allure.Net.Commons.dll`: + ```xml + + + PreserveNewest + + + ``` -Raw json configuration can be accessed from `AllureLifeCycle.Instance.JsonConfiguration` to extend configuration by adapters. See extension example here: https://github.com/allure-framework/allure-csharp/blob/bdf11bd3e1f41fd1e4a8fd22fa465b90b68e9d3f/Allure.SpecFlowPlugin/PluginHelper.cs#L20-L29 +The Allure lifecycle will start with the default configuration settings if no `allureConfig.json` is provided. +The unparsed configuration can be accessed via +`AllureLifeCycle.Instance.JsonConfiguration`. Adapters can use it to read +extended configuration properties they need. Check an example +[here](https://github.com/allure-framework/allure-csharp/blob/bf869a27828fa9b374b1e27dd972eb702be7d864/Allure.Xunit/AllureXunitConfiguration.cs#L33-L36). -Base configuration params are stored in `AllureLifeCycle.Instance.Configuration` -Allure configuration section is used to setup output directory and link patterns, e.g.: -``` +The parsed configuration object can be accessed via +`AllureLifeCycle.Instance.Configuration`. + +An example of the configuration file is provided below: + +```json { "allure": { - "directory": "allure-results", // optional, default value is "allure-results" - "title": "custom run title", // optional - "links": //optional + "directory": "allure-results", + "title": "custom run title", + "links": [ "https://example.org/{link}", "https://example.org/{issue}", "https://example.org/{tms}" + ], + "failExceptions": [ + "MyNamespace.MyAssertionException" ] } } ``` -All link pattern placeholders will be replaced with URL value of corresponding link type, e.g. +The `directory` property defaults to `"allure-results"`. + +All link pattern placeholders will be replaced with the URL value of the +corresponding link type. Given the configuration above, the following +transformation will be made: + +``` +link(type: "issue", url: "BUG-01") => https://example.org/BUG-01 +``` + +`failExceptions` must be an array of strings, each representing the full name of +an exception type. If an unhandled exception occurs whose type matches one of +the provided types, the test/step/fixture is considered failed. Otherwise, it's +considered broken. An exception's type matches a name if: -`link(type: "issue", url: "BUG-01") => https://example.org/BUG-01` + 1. Its full name equals the provided name, OR + 2. One of its base classes matches the name, OR + 3. It implements an interface that matches the name. ## Runtime API -Use this API it to enhance the report at runtime. +Use this API to enhance the report at runtime. ### The AllureApi facade @@ -123,11 +151,13 @@ functions. * `Step(string, Func>): T` - async step function. #### Noop step + * `Step(string)` #### Attachments + * AddAttachment - adds an attachment to the current step, fixture, or test. -* AddScreenDiff - adds needed artifacts to the current test case to be used with [screen-diff-plugin](https://github.com/allure-framework/allure2/tree/master/plugins/screen-diff-plugin) +* AddScreenDiff - adds needed artifacts to the current test case to be used with [screen-diff-plugin](https://github.com/allure-framework/allure2/tree/main/plugins/screen-diff-plugin) ### The ExtendedApi facade @@ -136,7 +166,7 @@ Use this class to access some less commonly used functions. #### Explicit step management > [!NOTE] -> Use the functions below only if lambda steps doesn't suit your needs. +> Use the functions below only if lambda steps don't suit your needs. * `StartStep(string): void` * `StartStep(string, Action): void` @@ -162,7 +192,7 @@ Use this class to access some less commonly used functions. #### Explicit fixture management > [!NOTE] -> Use the functions below only if lambda fixtures doesn't suit your needs. +> Use the functions below only if lambda fixtures don't suit your needs. * `StartBeforeFixture(string): void` * `StartAfterFixture(string): void` @@ -176,42 +206,45 @@ Use this class to access some less commonly used functions. ## The integration API -This API is designed for those who want to integrate Allure with a test -framework or library. You may still use it if you just want to improve the -report created from the tests you write, but we strongly recommend you to -consider using the end user API first. +This API is designed for adapter or library authors. You may still use it as a +test author, but we recommend considering the Runtime API first. ### AllureLifecycle -[AllureLifecycle](https://github.com/allure-framework/allure-csharp/blob/main/Allure.Commons/AllureLifecycle.cs) -class provides methods for test engine events processing. -Use `AllureLifecycle.Instance` property to access. +The [AllureLifecycle](https://github.com/allure-framework/allure-csharp/blob/main/Allure.Net.Commons/AllureLifecycle.cs) +class provides methods to manipulate the Allure context while responding to the +test framework's events. Use `AllureLifecycle.Instance` property to access it. + +#### Fixture context control -#### Fixture Events * StartBeforeFixture * StartAfterFixture * UpdateFixture * StopFixture -#### Testcase Events +#### Test context control + * ScheduleTestCase * StartTestCase * UpdateTestCase * StopTestCase * WriteTestCase -#### Step Events +#### Step context control + * StartStep * UpdateStep * StopStep #### Utility Methods + * CleanupResultDirectory - can be used in test run setup to clean old result files #### Context capturing + The methods above operate on the current Allure context. This context flows naturally as a part of ExecutionContext and is subject to the same -constraints. Particularly, changes made in an async callee can't be observed +constraints. Notably, changes made in an async callee can't be observed by the caller. Use the following methods of `AllureLifecycle` to capture the current Allure @@ -227,7 +260,7 @@ public static async Task Caller(ScenarioContext scenario) { await Callee(scenario); AllureLifecycle.Instance.RunInContext( - scenario.Get(), + scenario.Get(), // Get the previously captured context () => { // The test context required by the below methods wouldn't be @@ -244,7 +277,7 @@ public static async Task Callee(ScenarioContext scenario) new(){ uuid = Guid.NewGuid().ToString() } ); - // Pass Allure context to the caller via ScenarioContext + // Capture the context in an object of the test framework's object model scenario.Set(AllureLifecycle.Instance.Context); } ``` diff --git a/Allure.Features/Allure.Features.csproj b/Allure.SpecFlow.Tests.Samples/Allure.SpecFlow.Tests.Samples.csproj similarity index 90% rename from Allure.Features/Allure.Features.csproj rename to Allure.SpecFlow.Tests.Samples/Allure.SpecFlow.Tests.Samples.csproj index 3b6001fa..fffc15d0 100644 --- a/Allure.Features/Allure.Features.csproj +++ b/Allure.SpecFlow.Tests.Samples/Allure.SpecFlow.Tests.Samples.csproj @@ -1,7 +1,6 @@  net6.0 - bin @@ -18,7 +17,7 @@ - + diff --git a/Allure.Features/AssemblyInfo.cs b/Allure.SpecFlow.Tests.Samples/AssemblyInfo.cs similarity index 100% rename from Allure.Features/AssemblyInfo.cs rename to Allure.SpecFlow.Tests.Samples/AssemblyInfo.cs diff --git a/Allure.Features/IgnoreException.cs b/Allure.SpecFlow.Tests.Samples/IgnoreException.cs similarity index 66% rename from Allure.Features/IgnoreException.cs rename to Allure.SpecFlow.Tests.Samples/IgnoreException.cs index 8df881b5..71060e42 100644 --- a/Allure.Features/IgnoreException.cs +++ b/Allure.SpecFlow.Tests.Samples/IgnoreException.cs @@ -1,6 +1,6 @@ using System; -namespace Allure.Features +namespace Allure.SpecFlow.Tests.Samples { internal class IgnoreException : Exception { diff --git a/Allure.SpecFlow.Tests.Samples/Steps.cs b/Allure.SpecFlow.Tests.Samples/Steps.cs new file mode 100644 index 00000000..b2f117cf --- /dev/null +++ b/Allure.SpecFlow.Tests.Samples/Steps.cs @@ -0,0 +1,124 @@ +using System; +using System.IO; +using System.Linq; +using Allure.Net.Commons; +using Allure.SpecFlowPlugin; +using NUnit.Framework; +using TechTalk.SpecFlow; + +namespace Allure.SpecFlow.Tests.Samples +{ + public enum TestOutcome + { + passed, + failed, + broken + } + + [Binding] + public class Steps + { + private readonly ScenarioContext scenarioContext; + + public Steps(ScenarioContext scenarioContext) + { + this.scenarioContext = scenarioContext; + } + + [StepDefinition(@"Step is '(.*)'")] + public void StepResultIs(TestOutcome outcome) + { + switch (outcome) + { + case TestOutcome.passed: + break; + case TestOutcome.failed: + Assert.Fail("Failed"); + break; + case TestOutcome.broken: + throw new Exception("This test is failed", + new InvalidOperationException("Internal message", + new ArgumentException("One more message"))); + default: + throw new ArgumentException("value is not supported"); + } + } + + [StepDefinition("Step with attachment")] + public void StepWithAttach() + { + var path = Guid.NewGuid().ToString(); + File.WriteAllText(path, "hi there"); + AllureApi.AddAttachment(path); + } + + [StepDefinition("Step with table")] + public void StepWithTable(Table table) + { + } + + [StepDefinition("Step with params: (.*)")] + public void StepWithArgs(int number, string text) + { + } + + [BeforeTestRun] + public static void BeforeTestRun() + { + AllureLifecycle.Instance.CleanupResultDirectory(); + } + [BeforeFeature("beforefeaturepassed", Order = 1)] + public static void PassedBeforeFeature() + { + } + + [AfterFeature("afterfeaturepassed", Order = 1)] + public static void PassedAfterFeature() + { + } + + [BeforeFeature("beforefeaturefailed")] + public static void FailedBeforeFeature() + { + throw new Exception("Failed Before Feature"); + } + + [AfterFeature("afterfeaturefailed")] + public static void FailedAfterFeature() + { + throw new Exception("Failed After Feature"); + } + + [BeforeScenario(tags: "beforescenario")] + [AfterScenario(tags: "afterscenario")] + [BeforeStep(tags: "beforestep")] + [AfterStep(tags: "afterstep")] + public void HandleIt() + { + Handle(scenarioContext.ScenarioInfo.Tags); + } + + private static void Handle(string[] tags) + { + if (tags != null && tags.Contains("attachment")) + { + var path = $"{Guid.NewGuid().ToString()}.txt"; + File.WriteAllText(path, "hi there"); + AllureApi.AddAttachment(path); + AllureApi.AddAttachment(path, "text file"); + } + + if (tags != null) + { + if (tags.Any(x => x.EndsWith(Status.failed.ToString()) || x.EndsWith(Status.broken.ToString()))) + { + throw new Exception("Wasted"); + } + else if (tags.Any(x => x == PluginHelper.IGNORE_EXCEPTION)) + { + throw new IgnoreException(); + } + } + } + } +} \ No newline at end of file diff --git a/Allure.Features/TestData/After Feature Failure.feature b/Allure.SpecFlow.Tests.Samples/TestData/After Feature Failure.feature similarity index 100% rename from Allure.Features/TestData/After Feature Failure.feature rename to Allure.SpecFlow.Tests.Samples/TestData/After Feature Failure.feature diff --git a/Allure.Features/TestData/Attachments.feature b/Allure.SpecFlow.Tests.Samples/TestData/Attachments.feature similarity index 100% rename from Allure.Features/TestData/Attachments.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Attachments.feature diff --git a/Allure.Features/TestData/Before Feature Failure.feature b/Allure.SpecFlow.Tests.Samples/TestData/Before Feature Failure.feature similarity index 100% rename from Allure.Features/TestData/Before Feature Failure.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Before Feature Failure.feature diff --git a/Allure.Features/TestData/Ignored.feature b/Allure.SpecFlow.Tests.Samples/TestData/Ignored.feature similarity index 100% rename from Allure.Features/TestData/Ignored.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Ignored.feature diff --git a/Allure.Features/TestData/Invalid Steps.feature b/Allure.SpecFlow.Tests.Samples/TestData/Invalid Steps.feature similarity index 72% rename from Allure.Features/TestData/Invalid Steps.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Invalid Steps.feature index c94b65c6..439f7f1b 100644 --- a/Allure.Features/TestData/Invalid Steps.feature +++ b/Allure.SpecFlow.Tests.Samples/TestData/Invalid Steps.feature @@ -18,3 +18,10 @@ Given I don't have such step Given Step is 'passed' And I don't have such step too + + @broken + Scenario: Broken step followed by invalid step + Given Step is 'broken' + Given I don't have such step + Given Step is 'passed' + And I don't have such step too diff --git a/Allure.Features/TestData/Labels.feature b/Allure.SpecFlow.Tests.Samples/TestData/Labels.feature similarity index 100% rename from Allure.Features/TestData/Labels.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Labels.feature diff --git a/Allure.Features/TestData/Scenario Hooks.feature b/Allure.SpecFlow.Tests.Samples/TestData/Scenario Hooks.feature similarity index 100% rename from Allure.Features/TestData/Scenario Hooks.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Scenario Hooks.feature diff --git a/Allure.Features/TestData/Scenarios and Steps.feature b/Allure.SpecFlow.Tests.Samples/TestData/Scenarios and Steps.feature similarity index 100% rename from Allure.Features/TestData/Scenarios and Steps.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Scenarios and Steps.feature diff --git a/Allure.Features/TestData/Step arguments.feature b/Allure.SpecFlow.Tests.Samples/TestData/Step arguments.feature similarity index 100% rename from Allure.Features/TestData/Step arguments.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Step arguments.feature diff --git a/Allure.Features/TestData/Tags.feature b/Allure.SpecFlow.Tests.Samples/TestData/Tags.feature similarity index 100% rename from Allure.Features/TestData/Tags.feature rename to Allure.SpecFlow.Tests.Samples/TestData/Tags.feature diff --git "a/Allure.Features/TestData/\320\244\320\270\321\207\320\260 \321\201 \320\272\320\270\321\200\320\270\320\273\320\273\320\270\321\206\320\265\320\271.feature" "b/Allure.SpecFlow.Tests.Samples/TestData/\320\244\320\270\321\207\320\260 \321\201 \320\272\320\270\321\200\320\270\320\273\320\273\320\270\321\206\320\265\320\271.feature" similarity index 100% rename from "Allure.Features/TestData/\320\244\320\270\321\207\320\260 \321\201 \320\272\320\270\321\200\320\270\320\273\320\273\320\270\321\206\320\265\320\271.feature" rename to "Allure.SpecFlow.Tests.Samples/TestData/\320\244\320\270\321\207\320\260 \321\201 \320\272\320\270\321\200\320\270\320\273\320\273\320\270\321\206\320\265\320\271.feature" diff --git a/Allure.Features/allureConfig.json b/Allure.SpecFlow.Tests.Samples/allureConfig.json similarity index 100% rename from Allure.Features/allureConfig.json rename to Allure.SpecFlow.Tests.Samples/allureConfig.json diff --git a/Allure.Features/specflow.json b/Allure.SpecFlow.Tests.Samples/specflow.json similarity index 73% rename from Allure.Features/specflow.json rename to Allure.SpecFlow.Tests.Samples/specflow.json index eaab3380..525ccf97 100644 --- a/Allure.Features/specflow.json +++ b/Allure.SpecFlow.Tests.Samples/specflow.json @@ -1,8 +1,5 @@ { "stepAssemblies": [ - { - "assembly": "Allure.Features" - }, { "assembly": "Allure.SpecFlowPlugin" }, diff --git a/Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj b/Allure.SpecFlow.Tests/Allure.SpecFlow.Tests.csproj similarity index 90% rename from Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj rename to Allure.SpecFlow.Tests/Allure.SpecFlow.Tests.csproj index ea25f7e8..179eac5a 100644 --- a/Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj +++ b/Allure.SpecFlow.Tests/Allure.SpecFlow.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/Allure.SpecFlow.Tests/ConfigurationTests.cs b/Allure.SpecFlow.Tests/ConfigurationTests.cs new file mode 100644 index 00000000..ef49f2ac --- /dev/null +++ b/Allure.SpecFlow.Tests/ConfigurationTests.cs @@ -0,0 +1,27 @@ +using NUnit.Framework; +using System.IO; +using Allure.SpecFlowPlugin; + +namespace Allure.SpecFlow.Tests +{ + [TestFixture] + public class ConfigurationTests + { + [TestCase(@"allureConfig.json")] + [TestCase(@"allureConfigStepArguments.json")] + [TestCase(@"allureConfigWithInvalidRegex.json")] + public void ShouldNotHaveNullParents(string json) + { + var config = PluginHelper.GetConfiguration(File.ReadAllText(json)); + Assert.Multiple(() => + { + Assert.That(config.grouping.behaviors, Is.Not.Null); + Assert.That(config.grouping.packages, Is.Not.Null); + Assert.That(config.grouping.suites, Is.Not.Null); + Assert.That(config.labels, Is.Not.Null); + Assert.That(config.links, Is.Not.Null); + Assert.That(config.stepArguments, Is.Not.Null); + }); + } + } +} \ No newline at end of file diff --git a/Allure.SpecFlow.Tests/IntegrationTests.cs b/Allure.SpecFlow.Tests/IntegrationTests.cs new file mode 100644 index 00000000..00c0d483 --- /dev/null +++ b/Allure.SpecFlow.Tests/IntegrationTests.cs @@ -0,0 +1,286 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using Allure.Net.Commons; +using Gherkin; +using Gherkin.Ast; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Allure.SpecFlow.Tests +{ + [TestFixture] + public class IntegrationFixture + { + private FileInfo allureConfigFile; + private DirectoryInfo allureResultsDir; + private readonly HashSet allureContainers = new(); + private readonly HashSet allureTestResults = new(); + private IDictionary> scenariosByStatus; + + [OneTimeSetUp] + public void Init() + { + var samplesProjectDir = Path.GetFullPath( + Path.Combine( + AppDomain.CurrentDomain.BaseDirectory, + @"./../../../../Allure.SpecFlow.Tests.Samples" + ) + ); + this.allureResultsDir = Directory.CreateDirectory( + Path.Combine( + Path.GetTempPath(), + Guid.NewGuid().ToString() + ) + ); + this.allureConfigFile = PrepareAllureConfig( + Path.Combine( + samplesProjectDir, + "allureConfig.json" + ), + this.allureResultsDir + ); + + RunSamples(samplesProjectDir, this.allureConfigFile); + + var featuresDirectory = Path.Combine(samplesProjectDir, "TestData"); + + // parse allure suites + ParseAllureSuites(this.allureResultsDir.FullName); + ParseFeatures(featuresDirectory); + } + + [OneTimeTearDown] + public void RemoveTempFiles() + { + if (this.allureResultsDir.Exists) + { + this.allureResultsDir.Delete(recursive: true); + } + if (this.allureConfigFile.Exists) + { + this.allureConfigFile.Delete(); + } + } + + static FileInfo PrepareAllureConfig(string originalConfigPath, DirectoryInfo resultsDir) + { + var json = JObject.Parse( + File.ReadAllText(originalConfigPath) + ); + + if (json["allure"] is not JObject allureJsonConfig) + { + throw new InvalidOperationException($"Unexpected format of {originalConfigPath}"); + } + + var path = Path.Combine( + Path.GetTempPath(), + Guid.NewGuid().ToString() + ); + allureJsonConfig["directory"] = new JValue(resultsDir.FullName); + File.WriteAllText(path, json.ToString()); + return new FileInfo(path); + } + + static void RunSamples(string samplesProjectDir, FileInfo allureConfigFile) + { + var stdout = new StringBuilder(); + var stderr = new StringBuilder(); + var configuration = Assembly.GetExecutingAssembly() + .GetCustomAttribute() + ?.Configuration + ?? "Debug"; + var dotnetTestProcessInfo = new ProcessStartInfo + { + WorkingDirectory = samplesProjectDir, + FileName = "dotnet", + Arguments = $"test --configuration {configuration}", + }; + dotnetTestProcessInfo.Environment[AllureConstants.ALLURE_CONFIG_ENV_VARIABLE] = + allureConfigFile.FullName; + var p = new Process { StartInfo = dotnetTestProcessInfo }; + p.Start(); + p.WaitForExit(); + } + + [TestCase(Status.passed)] + [TestCase(Status.failed)] + [TestCase(Status.broken)] + [TestCase(Status.skipped)] + public void TestStatus(Status status) + { + var expected = scenariosByStatus[status.ToString()]; + var actual = allureTestResults.Where(x => x.status == status).Select(x => x.name).ToList(); + Assert.That(actual, Is.EquivalentTo(expected)); + } + + private void ParseFeatures(string featuresDir) + { + var parser = new Parser(); + var scenarios = new List(); + var features = new DirectoryInfo(featuresDir).GetFiles("*.feature"); + scenarios.AddRange(features.SelectMany(f => + { + var children = parser.Parse(f.FullName).Feature.Children.ToList(); + var scenarioOutlines = children.Where( + x => (x as dynamic).Examples.Length > 0 + ).ToList(); + foreach (var s in scenarioOutlines) + { + var examplesCount = (s as dynamic).Examples[0] + .TableBody.Length; + for (int i = 1; i < examplesCount; i++) + { + children.Add(s); + } + } + return children; + }) + .Select(x => x as Scenario)); + + scenariosByStatus = scenarios.GroupBy( + x => x.Tags.FirstOrDefault( + x => Enum.GetNames( + typeof(Status) + ).Contains( + x.Name.Replace("@", "") + ) + )?.Name.Replace("@", "") ?? "_notag_", + x => x.Name + ).ToDictionary(g => g.Key, g => g.ToList()); + + // Extra placeholder scenario for testing an exception in AfterFeature + scenariosByStatus["broken"].Add("Feature hook failure placeholder"); + } + + private void ParseAllureSuites(string allureResultsDir) + { + var allureTestResultFiles = new DirectoryInfo(allureResultsDir).GetFiles("*-result.json"); + var allureContainerFiles = new DirectoryInfo(allureResultsDir).GetFiles("*-container.json"); + var serializer = new JsonSerializer(); + + foreach (var fileInfo in allureContainerFiles) + { + using var file = File.OpenText(fileInfo.FullName); + var container = (TestResultContainer)serializer.Deserialize(file, typeof(TestResultContainer)); + allureContainers.Add(container); + } + + foreach (var fileInfo in allureTestResultFiles) + { + using var file = File.OpenText(fileInfo.FullName); + var testResult = (TestResult)serializer.Deserialize(file, typeof(TestResult)); + allureTestResults.Add(testResult); + } + } + + [Test] + public void ShouldConvertTableToStepParams() + { + var parameters = allureTestResults + .First(x => x.name == "Table arguments").steps.SelectMany(s => s.parameters); + + Assert.That(parameters.Select(x => x.name), Has.Exactly(1).EqualTo("name")); + Assert.That(parameters.Select(x => x.name), Has.Exactly(1).EqualTo("surname")); + Assert.That(parameters.Select(x => x.name), Has.Exactly(2).EqualTo("width")); + Assert.That(parameters.Select(x => x.name), Has.Exactly(0).EqualTo("attribute")); + } + + [Test] + public void ShouldGroupNestedSteps() + { + var nestedSteps = allureTestResults + .First(x => x.name == "Shared Steps").steps + .SelectMany(x => x.steps); + + Assert.That(nestedSteps, Has.Exactly(1).Items); + } + + [Test] + public void ShouldNotDuplicateAfterFixtures() + { + var afters = allureContainers.Select(x => x.afters.Select(y => y.name)); + Assert.That(afters, Is.All.Unique); + } + + [Test] + public void ShouldNotDuplicateBeforeFixtures() + { + var befores = allureContainers.Select(x => x.befores.Select(y => y.name)); + Assert.That(befores, Is.All.Unique); + } + + [Test] + public void ShouldParseLinks() + { + var scenarios = allureTestResults + .Where(x => x.labels.Any(l => l.value == "labels")); + + var links = scenarios.SelectMany(x => x.links); + Assert.Multiple(() => + { + Assert.That(links.Select(x => x.url), Has.One.EqualTo("http://example.org")); + Assert.That(links.Where(x => x.type == "tms").Select(x => x.url), + Has.One.EqualTo("https://example.org/234")); + Assert.That(links.Where(x => x.type == "issue").Select(x => x.url), + Has.One.EqualTo("https://example.org/999999").And.One.EqualTo("https://example.org/123")); + }); + } + + [Test] + public void ShouldParseTags() + { + var scenarios = allureTestResults + .Where(x => x.labels.Any(l => l.value == "labels")); + + var labels = scenarios.SelectMany(x => x.labels); + Assert.Multiple(() => + { + // all selected scenarios should have only 2 unmatched tags - "labels" and "passed". One scenario also has "tag1" as unmatched. + Assert.That(labels.Where(x => x.name == "tag"), Has.Exactly(scenarios.Count() * 2 + 1).Items); + // owner + Assert.That(labels.Where(x => x.value == "Vasya").Select(l => l.name), + Has.Exactly(scenarios.Count()).Items.And.All.EqualTo("owner")); + }); + } + + [Test] + public void ShouldParseCustomLabel() + { + var scenarios = allureTestResults + .Where(x => x.labels.Any(l => l.value == "labels")); + + var labels = scenarios.SelectMany(x => x.labels); + + Assert.That(labels.Where(x => x.value == "pepa").Select(l => l.name), + Has.Exactly(1).Items.And.All.EqualTo("custom_label")); + } + + [Test] + public void ShouldAddParametersForScenarioExamples() + { + var parameters = allureTestResults.Where(x => x.name == "Scenario with examples").SelectMany(x => x.parameters).ToArray(); + Assert.That(parameters, + Has.Exactly(1).Items.Matches(x => x.name == "id" && x.value == "\"1\""). + And.Exactly(1).Items.Matches(x => x.name == "name" && x.value == "\"John\"")); + + Assert.That(parameters, + Has.Exactly(1).Items.Matches(x => x.name == "id" && x.value == "\"2\""). + And.Exactly(1).Items.Matches(x => x.name == "name" && x.value == "\"Alex\"")); + } + + [Test] + public void ShouldReadHostNameFromConfigTitle() + { + var hostNames = allureTestResults + .SelectMany(x => x.labels.Where(l => l.name == "host").Select(l => l.value)).Distinct(); + Assert.That(hostNames, Has.One.Items.And.All.EqualTo("5994A3F7-AF84-46AD-9393-000BB45553CC")); + } + } +} \ No newline at end of file diff --git a/Allure.SpecFlow.Tests/TestSetup.cs b/Allure.SpecFlow.Tests/TestSetup.cs new file mode 100644 index 00000000..363c1de2 --- /dev/null +++ b/Allure.SpecFlow.Tests/TestSetup.cs @@ -0,0 +1,17 @@ +using NUnit.Framework; +using System; +using System.IO; + +namespace Allure.SpecFlow.Tests +{ + [SetUpFixture] + public class TestSetup + { + [OneTimeSetUp] + public void Setup() + { + // setup current folder for nUnit engine + Environment.CurrentDirectory = Path.GetDirectoryName(typeof(TestSetup).Assembly.Location); + } + } +} \ No newline at end of file diff --git a/Allure.SpecFlowPlugin.Tests/allureConfig.json b/Allure.SpecFlow.Tests/allureConfig.json similarity index 100% rename from Allure.SpecFlowPlugin.Tests/allureConfig.json rename to Allure.SpecFlow.Tests/allureConfig.json diff --git a/Allure.SpecFlowPlugin.Tests/allureConfigStepArguments.json b/Allure.SpecFlow.Tests/allureConfigStepArguments.json similarity index 100% rename from Allure.SpecFlowPlugin.Tests/allureConfigStepArguments.json rename to Allure.SpecFlow.Tests/allureConfigStepArguments.json diff --git a/Allure.SpecFlowPlugin.Tests/allureConfigWithInvalidRegex.json b/Allure.SpecFlow.Tests/allureConfigWithInvalidRegex.json similarity index 100% rename from Allure.SpecFlowPlugin.Tests/allureConfigWithInvalidRegex.json rename to Allure.SpecFlow.Tests/allureConfigWithInvalidRegex.json diff --git a/Allure.SpecFlowPlugin/Allure.SpecFlowPlugin.csproj b/Allure.SpecFlow/Allure.SpecFlow.csproj similarity index 90% rename from Allure.SpecFlowPlugin/Allure.SpecFlowPlugin.csproj rename to Allure.SpecFlow/Allure.SpecFlow.csproj index 3e5c9276..f24182c6 100644 --- a/Allure.SpecFlowPlugin/Allure.SpecFlowPlugin.csproj +++ b/Allure.SpecFlow/Allure.SpecFlow.csproj @@ -1,10 +1,11 @@  + Allure.SpecFlowPlugin + Allure.SpecFlowPlugin netstandard2.0 true enable - Allure.SpecFlow Alexander Bakanov Create beautiful reports from your SpecFlow tests. Allure-SF-Color.png diff --git a/Allure.SpecFlowPlugin/AllureBindingInvoker.cs b/Allure.SpecFlow/AllureBindingInvoker.cs similarity index 91% rename from Allure.SpecFlowPlugin/AllureBindingInvoker.cs rename to Allure.SpecFlow/AllureBindingInvoker.cs index 635c4ef4..e65a59c7 100644 --- a/Allure.SpecFlowPlugin/AllureBindingInvoker.cs +++ b/Allure.SpecFlow/AllureBindingInvoker.cs @@ -26,6 +26,15 @@ internal class AllureBindingInvoker : BindingInvoker const string PLACEHOLDER_TESTCASE_KEY = "Allure.SpecFlowPlugin.HAS_PLACEHOLDER_TESTCASE"; + const string ASSERT_EXC_NUNIT = + "NUnit.Framework.AssertionException"; + const string ASSERT_EXC_XUNIT_NEW = // From v2.4.2 and onward. + "Xunit.Sdk.IAssertionException"; + const string ASSERT_EXC_XUNIT_OLD = // Prior to v2.4.2 + "Xunit.Sdk.XunitException"; + const string ASSERT_EXC_MSTEST = + "Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException"; + static readonly AllureLifecycle allure = AllureLifecycle.Instance; readonly ITestRunnerManager testRunnerManager; @@ -47,6 +56,13 @@ IUnitTestRuntimeProvider unitTestRuntimeProvider unitTestRuntimeProvider, WriteErrorToFileSafe ); + AllureLifecycle.Instance.AllureConfiguration.FailExceptions ??= new() + { + ASSERT_EXC_NUNIT, + ASSERT_EXC_XUNIT_NEW, + ASSERT_EXC_XUNIT_OLD, + ASSERT_EXC_MSTEST + }; } public override object InvokeBinding( @@ -181,7 +197,7 @@ ITestTracer testTracer } catch (Exception ex) { - ReportStepError(ex); + PropagateErrorUpToTest(ex); throw; } } @@ -393,8 +409,8 @@ void ReportFeatureFixtureError( Exception error ) { - var makeBroken = WrapMakeBroken(error); - allure.StopFixture(makeBroken); + var updateStatus = WrapErrorStatus(error); + allure.StopFixture(updateStatus); // Create one placeholder test case per failed feature-level hook // to indicate the error. @@ -417,7 +433,7 @@ Exception error ); allure - .StopTestCase(makeBroken) + .StopTestCase(updateStatus) .WriteTestCase(); featureContext.Add(PLACEHOLDER_TESTCASE_KEY, true); @@ -428,7 +444,7 @@ static void ReportScenarioFixtureError(Exception error) { var status = PluginHelper.IsIgnoreException(error) ? Status.skipped - : Status.broken; + : PluginHelper.ResolveErrorStatus(error); var statusDetails = PluginHelper.GetStatusDetails(error); allure.StopFixture( @@ -452,13 +468,13 @@ static void ReportScenarioFixtureError(Exception error) } } - static void ReportStepError(Exception error) + static void PropagateErrorUpToTest(Exception error) { if (allure.Context.HasStep) { - MakeStepBroken(error); + ReportStepError(error); } - MakeTestCaseBroken(error); + ReportTestCaseError(error); } static void StartBeforeFixture(HookBinding hook) => @@ -474,25 +490,25 @@ static void StartAfterFixture(HookBinding hook) => static bool IsAllureHook(HookBinding hook) => hook.Method.Type.FullName == typeof(AllureBindings).FullName; - static Action WrapMakeBroken(Exception error) => - PluginHelper.WrapStatusOverwrite( - Status.broken, - PluginHelper.GetStatusDetails(error), - Status.none, - Status.passed - ); - static void MakePassed(ExecutableItem item) => item.status = Status.passed; - static void MakeTestCaseBroken(Exception error) => + static void ReportTestCaseError(Exception error) => allure.UpdateTestCase( - WrapMakeBroken(error) + WrapErrorStatus(error) ); - static void MakeStepBroken(Exception error) => + static void ReportStepError(Exception error) => allure.UpdateStep( - WrapMakeBroken(error) + WrapErrorStatus(error) + ); + + static Action WrapErrorStatus(Exception error) => + PluginHelper.WrapStatusOverwrite( + PluginHelper.ResolveErrorStatus(error), + PluginHelper.GetStatusDetails(error), + Status.none, + Status.passed ); } } \ No newline at end of file diff --git a/Allure.SpecFlowPlugin/AllureBindings.cs b/Allure.SpecFlow/AllureBindings.cs similarity index 100% rename from Allure.SpecFlowPlugin/AllureBindings.cs rename to Allure.SpecFlow/AllureBindings.cs diff --git a/Allure.SpecFlowPlugin/AllurePlugin.cs b/Allure.SpecFlow/AllurePlugin.cs similarity index 100% rename from Allure.SpecFlowPlugin/AllurePlugin.cs rename to Allure.SpecFlow/AllurePlugin.cs diff --git a/Allure.SpecFlowPlugin/AllureTestTracerWrapper.cs b/Allure.SpecFlow/AllureTestTracerWrapper.cs similarity index 95% rename from Allure.SpecFlowPlugin/AllureTestTracerWrapper.cs rename to Allure.SpecFlow/AllureTestTracerWrapper.cs index 249465b8..25b721d3 100644 --- a/Allure.SpecFlowPlugin/AllureTestTracerWrapper.cs +++ b/Allure.SpecFlow/AllureTestTracerWrapper.cs @@ -6,6 +6,7 @@ using System.Text; using System.Text.RegularExpressions; using Allure.Net.Commons; +using Allure.Net.Commons.Functions; using CsvHelper; using TechTalk.SpecFlow; using TechTalk.SpecFlow.Bindings; @@ -62,22 +63,23 @@ TimeSpan duration void ITestTracer.TraceError(Exception ex, TimeSpan duration) { this.TraceError(ex, duration); + var status = PluginHelper.ResolveErrorStatus(ex); allure.StopStep( - PluginHelper.WrapStatusInit(Status.failed, ex) + PluginHelper.WrapStatusInit(status, ex) ); - FailScenario(ex); + FailScenario(status, ex); } void ITestTracer.TraceStepSkipped() { this.TraceStepSkipped(); - allure.StopStep(x => x.status = Status.skipped); + ExtendedApi.SkipStep(); } void ITestTracer.TraceStepPending(BindingMatch match, object[] arguments) { this.TraceStepPending(match, arguments); - allure.StopStep(x => x.status = Status.skipped); + ExtendedApi.SkipStep(); } void ITestTracer.TraceNoMatchingStepDefinition( @@ -218,11 +220,11 @@ private void StartStep(StepInstance stepInstance) AllureApi.AddAttachment("table", "text/csv", ms.ToArray(), ".csv"); } - private static void FailScenario(Exception ex) + private static void FailScenario(Status status, Exception ex) { allure.UpdateTestCase(x => { - x.status = x.status != Status.none ? x.status : Status.failed; + x.status = x.status != Status.none ? x.status : status; x.statusDetails = PluginHelper.GetStatusDetails(ex); }); } diff --git a/Allure.SpecFlowPlugin/App.config.transform b/Allure.SpecFlow/App.config.transform similarity index 100% rename from Allure.SpecFlowPlugin/App.config.transform rename to Allure.SpecFlow/App.config.transform diff --git a/Allure.SpecFlowPlugin/PluginConfiguration.cs b/Allure.SpecFlow/PluginConfiguration.cs similarity index 100% rename from Allure.SpecFlowPlugin/PluginConfiguration.cs rename to Allure.SpecFlow/PluginConfiguration.cs diff --git a/Allure.SpecFlowPlugin/PluginHelper.cs b/Allure.SpecFlow/PluginHelper.cs similarity index 97% rename from Allure.SpecFlowPlugin/PluginHelper.cs rename to Allure.SpecFlow/PluginHelper.cs index 508b7d09..188c7ebf 100644 --- a/Allure.SpecFlowPlugin/PluginHelper.cs +++ b/Allure.SpecFlow/PluginHelper.cs @@ -92,6 +92,7 @@ internal static void StartTestCase( var testResult = new TestResult { name = title, + description = scenarioInfo.Description, labels = new List