From c227205e6e18e47da3dc46ae1b571297f8e51a37 Mon Sep 17 00:00:00 2001 From: Andreas Willich Date: Thu, 24 Oct 2019 09:30:37 +0200 Subject: [PATCH] Add app.config configuration for Cucumber-Messages (#1759) * update submodule * add specs and bindings * add app.config dtos * convert into SpecFlowConfiguration & unit tests for it * make app.config scenarios only available for full framework * update vswhere * fix cucumber messages specs * remove unnecessary csproj changes --- .../AppConfig/AppConfigConfigurationLoader.cs | 10 ++ .../AppConfig/ConfigurationSectionHandler.cs | 13 +- .../AppConfig/CucumberMessageSinkElement.cs | 21 +++ .../AppConfig/CucumberMessagesElement.cs | 23 ++++ .../CucumberMessagesSinkCollection.cs | 18 +++ .../Configuration/AppConfigTests.cs | 124 ++++++++++++++++++ .../Sinks/ProtobufFileSinkOutputTests.cs | 2 +- ...-Messages App.Config Configuration.feature | 81 ++++++++++++ .../SpecFlowConfigurationSteps.cs | 7 + .../TechTalk.SpecFlow.Specs.csproj | 1 + 10 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessageSinkElement.cs create mode 100644 TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesElement.cs create mode 100644 TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesSinkCollection.cs create mode 100644 Tests/TechTalk.SpecFlow.Specs/Features/Configuration/Cucumber-Messages App.Config Configuration.feature diff --git a/TechTalk.SpecFlow/Configuration/AppConfig/AppConfigConfigurationLoader.cs b/TechTalk.SpecFlow/Configuration/AppConfig/AppConfigConfigurationLoader.cs index fb37a9de7..48eefbf58 100644 --- a/TechTalk.SpecFlow/Configuration/AppConfig/AppConfigConfigurationLoader.cs +++ b/TechTalk.SpecFlow/Configuration/AppConfig/AppConfigConfigurationLoader.cs @@ -95,6 +95,16 @@ public Configuration.SpecFlowConfiguration LoadAppConfig(Configuration.SpecFlowC additionalStepAssemblies.Add(assemblyName); } + if (IsSpecified(configSection.CucumberMessages)) + { + cucumberMessagesConfiguration.Enabled = configSection.CucumberMessages.Enabled; + + foreach (CucumberMessageSinkElement cucumberMessagesSink in configSection.CucumberMessages.Sinks) + { + cucumberMessagesConfiguration.Sinks.Add(new CucumberMessagesSink(cucumberMessagesSink.Type, cucumberMessagesSink.Path)); + } + } + return new SpecFlowConfiguration(ConfigSource.AppConfig, runtimeContainerRegistrationCollection, generatorContainerRegistrationCollection, diff --git a/TechTalk.SpecFlow/Configuration/AppConfig/ConfigurationSectionHandler.cs b/TechTalk.SpecFlow/Configuration/AppConfig/ConfigurationSectionHandler.cs index 5a343228e..e14755a6a 100644 --- a/TechTalk.SpecFlow/Configuration/AppConfig/ConfigurationSectionHandler.cs +++ b/TechTalk.SpecFlow/Configuration/AppConfig/ConfigurationSectionHandler.cs @@ -12,8 +12,8 @@ public LanguageConfigElement Language { get { return (LanguageConfigElement)this["language"]; } set { this["language"] = value; } - } - + } + [ConfigurationProperty("bindingCulture", IsRequired = false)] public BindingCultureConfigElement BindingCulture { @@ -49,7 +49,14 @@ public StepAssemblyCollection StepAssemblies get { return (StepAssemblyCollection)this["stepAssemblies"]; } set { this["stepAssemblies"] = value; } } - + + [ConfigurationProperty("cucumber-messages", IsRequired = false)] + public CucumberMessagesElement CucumberMessages + { + get => (CucumberMessagesElement)this["cucumber-messages"]; + set => this["cucumber-messages"] = value; + } + public static ConfigurationSectionHandler CreateFromXml(string xmlContent) { ConfigurationSectionHandler section = new ConfigurationSectionHandler(); diff --git a/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessageSinkElement.cs b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessageSinkElement.cs new file mode 100644 index 000000000..2f57f3f9e --- /dev/null +++ b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessageSinkElement.cs @@ -0,0 +1,21 @@ +using System.Configuration; + +namespace TechTalk.SpecFlow.Configuration.AppConfig +{ + public class CucumberMessageSinkElement : ConfigurationElement + { + [ConfigurationProperty("type", IsRequired = true)] + public string Type + { + get => (string) this["type"]; + set => this["type"] = value; + } + + [ConfigurationProperty("path", IsRequired = true)] + public string Path + { + get => (string) this["path"]; + set => this["path"] = value; + } + } +} \ No newline at end of file diff --git a/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesElement.cs b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesElement.cs new file mode 100644 index 000000000..7f25adb22 --- /dev/null +++ b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesElement.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Configuration; + +namespace TechTalk.SpecFlow.Configuration.AppConfig +{ + public class CucumberMessagesElement : ConfigurationElement + { + [ConfigurationProperty("enabled", DefaultValue = false, IsRequired = false)] + public bool Enabled + { + get => (bool)this["enabled"]; + set => this["enabled"] = value; + } + + [ConfigurationProperty("sinks", IsDefaultCollection = false, IsRequired = false)] + [ConfigurationCollection(typeof(CucumberMessagesSinkCollection), AddItemName = "sink")] + public CucumberMessagesSinkCollection Sinks + { + get => (CucumberMessagesSinkCollection) this["sinks"]; + set => this["sinks"] = value; + } + } +} \ No newline at end of file diff --git a/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesSinkCollection.cs b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesSinkCollection.cs new file mode 100644 index 000000000..747e129b7 --- /dev/null +++ b/TechTalk.SpecFlow/Configuration/AppConfig/CucumberMessagesSinkCollection.cs @@ -0,0 +1,18 @@ +using System.Configuration; + +namespace TechTalk.SpecFlow.Configuration.AppConfig +{ + public class CucumberMessagesSinkCollection : ConfigurationElementCollection + { + protected override ConfigurationElement CreateNewElement() + { + return new CucumberMessageSinkElement(); + } + + protected override object GetElementKey(ConfigurationElement element) + { + var cucumberMessageSinkElement = ((CucumberMessageSinkElement)element); + return cucumberMessageSinkElement.Type + cucumberMessageSinkElement.Path; + } + } +} \ No newline at end of file diff --git a/Tests/TechTalk.SpecFlow.RuntimeTests/Configuration/AppConfigTests.cs b/Tests/TechTalk.SpecFlow.RuntimeTests/Configuration/AppConfigTests.cs index 611db9e8e..188326b3e 100644 --- a/Tests/TechTalk.SpecFlow.RuntimeTests/Configuration/AppConfigTests.cs +++ b/Tests/TechTalk.SpecFlow.RuntimeTests/Configuration/AppConfigTests.cs @@ -328,5 +328,129 @@ public void Check_StepAssemblies_TwoEntry() runtimeConfig.AdditionalStepAssemblies[1].Should().Be("testEntry2"); } + + [Fact] + public void Check_CucumberMessages_NotConfigured_EnabledIsFalse() + { + string config = @" + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Enabled.Should().BeFalse(); + } + + + [Fact] + public void Check_CucumberMessages_EmptyTag_EnabledIsFalse() + { + string config = @" + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Enabled.Should().BeFalse(); + } + + [Fact] + public void Check_CucumberMessages_Enabled_True() + { + string config = @" + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Enabled.Should().BeTrue(); + } + + [Fact] + public void Check_CucumberMessages_Enabled_False() + { + string config = @" + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Enabled.Should().BeFalse(); + } + + [Fact] + public void Check_CucumberMessages_Sinks_EmptyList() + { + string config = @" + + + + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Sinks.Should().BeEmpty(); + } + + [Fact] + public void Check_CucumberMessages_Sinks_ListOneEntry() + { + string config = @" + + + + + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Sinks.Count.Should().Be(1); + } + + [Fact] + public void Check_CucumberMessages_Sinks_ListMultipleEntry() + { + string config = @" + + + + + + + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + runtimeConfig.CucumberMessagesConfiguration.Sinks.Count.Should().Be(3); + } + + [Fact] + public void Check_CucumberMessages_Sinks_DataOfEntry() + { + string config = @" + + + + + + "; + + var configSection = ConfigurationSectionHandler.CreateFromXml(config); + + var runtimeConfig = new AppConfigConfigurationLoader().LoadAppConfig(ConfigurationLoader.GetDefault(), configSection); + var cucumberMessagesSink = runtimeConfig.CucumberMessagesConfiguration.Sinks.First(); + + cucumberMessagesSink.Type.Should().Be("file"); + cucumberMessagesSink.Path.Should().Be(@"C:\temp\testrun.cm"); + } } } diff --git a/Tests/TechTalk.SpecFlow.RuntimeTests/CucumberMessages/Sinks/ProtobufFileSinkOutputTests.cs b/Tests/TechTalk.SpecFlow.RuntimeTests/CucumberMessages/Sinks/ProtobufFileSinkOutputTests.cs index 916bfd8cd..28efc6c90 100644 --- a/Tests/TechTalk.SpecFlow.RuntimeTests/CucumberMessages/Sinks/ProtobufFileSinkOutputTests.cs +++ b/Tests/TechTalk.SpecFlow.RuntimeTests/CucumberMessages/Sinks/ProtobufFileSinkOutputTests.cs @@ -82,7 +82,7 @@ public MemoryStream GetWritableStream() return new MemoryStream(); } - public ProtobufFileSinkConfiguration GetProtobufFileSinkConfiguration(string targetFilePath = "CucumberMessageQueue") + public ProtobufFileSinkConfiguration GetProtobufFileSinkConfiguration(string targetFilePath = "cucumbermessages") { return new ProtobufFileSinkConfiguration(targetFilePath); } diff --git a/Tests/TechTalk.SpecFlow.Specs/Features/Configuration/Cucumber-Messages App.Config Configuration.feature b/Tests/TechTalk.SpecFlow.Specs/Features/Configuration/Cucumber-Messages App.Config Configuration.feature new file mode 100644 index 000000000..10b2a3eac --- /dev/null +++ b/Tests/TechTalk.SpecFlow.Specs/Features/Configuration/Cucumber-Messages App.Config Configuration.feature @@ -0,0 +1,81 @@ +@fullframework +Feature: Cucumber-Messages Configuration via app.config + + Cucumber-messages can be configured in the SpecFlow section of the app.config file to enable/disable messages + and configure alternative output sinks + +Scenario: No configuration creates no output file + + Given there is a project with this app.config configuration + """ + + + +
+ + + + + + + """ + When the test suite is executed + Then no Cucumber-Messages file is created + +Scenario: Disabled configuration creates no output file + Given there is a project with this app.config configuration + """ + + + +
+ + + + + + + """ + When the test suite is executed + Then no Cucumber-Messages file is created + +Scenario: Enabled configuration with no sinks configured create default output file + The default Cucumber-Messages file is created at `cucumbermessages\messages` relative to the output directory + + Given there is a project with this app.config configuration + """ + + + +
+ + + + + + + """ + When the test suite is executed + Then the Cucumber-Messages file 'cucumbermessages\messages' is created + +Scenario: Configured sinks are respected + Given there is a project with this app.config configuration + """ + + + +
+ + + + + + + + + + + """ + When the test suite is executed + Then the Cucumber-Messages file 'custom_cucumber_messages_file.cm' is created + diff --git a/Tests/TechTalk.SpecFlow.Specs/StepDefinitions/SpecFlowConfigurationSteps.cs b/Tests/TechTalk.SpecFlow.Specs/StepDefinitions/SpecFlowConfigurationSteps.cs index 3668e7c41..1fa717304 100644 --- a/Tests/TechTalk.SpecFlow.Specs/StepDefinitions/SpecFlowConfigurationSteps.cs +++ b/Tests/TechTalk.SpecFlow.Specs/StepDefinitions/SpecFlowConfigurationSteps.cs @@ -46,6 +46,13 @@ public void GivenThereIsAProjectWithThisSpecFlowJsonConfiguration(string specFlo _testSuiteSetupDriver.AddSpecFlowJsonFromString(specFlowJson); } + [Given(@"there is a project with this app\.config configuration")] + public void GivenThereIsAProjectWithThisApp_ConfigConfiguration(string multilineText) + { + _testSuiteSetupDriver.AddAppConfigFromString(multilineText); + } + + [Given(@"the specflow configuration is")] public void GivenTheSpecFlowConfigurationIs(string specFlowSection) { diff --git a/Tests/TechTalk.SpecFlow.Specs/TechTalk.SpecFlow.Specs.csproj b/Tests/TechTalk.SpecFlow.Specs/TechTalk.SpecFlow.Specs.csproj index 7fb9ff5a1..b2acc40b0 100644 --- a/Tests/TechTalk.SpecFlow.Specs/TechTalk.SpecFlow.Specs.csproj +++ b/Tests/TechTalk.SpecFlow.Specs/TechTalk.SpecFlow.Specs.csproj @@ -104,6 +104,7 @@ + <_SpecFlow_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_SpecFlow_TaskFolder)' == ''">netcoreapp2.0 <_SpecFlow_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_SpecFlow_TaskFolder)' == ''">net471