diff --git a/IdeIntegration/Vs2010Integration/UI/GenerateStepDefinitionSkeletonForm.Designer.cs b/IdeIntegration/Vs2010Integration/UI/GenerateStepDefinitionSkeletonForm.Designer.cs index 70dc7a5e5..7c20cfa7e 100644 --- a/IdeIntegration/Vs2010Integration/UI/GenerateStepDefinitionSkeletonForm.Designer.cs +++ b/IdeIntegration/Vs2010Integration/UI/GenerateStepDefinitionSkeletonForm.Designer.cs @@ -197,6 +197,7 @@ private void InitializeComponent() // // helpLinkLabel // + this.helpLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.helpLinkLabel.AutoSize = true; this.helpLinkLabel.Location = new System.Drawing.Point(286, 312); this.helpLinkLabel.Name = "helpLinkLabel"; diff --git a/Languages.xml b/Languages.xml index 0a464a277..c797cc5f6 100644 --- a/Languages.xml +++ b/Languages.xml @@ -91,6 +91,7 @@ Beispiele Angenommen Gegeben sei + Gegeben seien Wenn Dann Und @@ -111,16 +112,16 @@ But - Crikey - Background - Mate - Blokes - Cobber - Ya know how - When - Ya gotta - N - Cept + Pretty much + First off + Awww, look mate + Reckon it's like + You'll wanna + Y'know + It's just unbelievable + But at the end of the day I reckon + Too right + Yeah nah OH HAI @@ -662,4 +663,4 @@ 並且 但是 - \ No newline at end of file + diff --git a/Reporting/MsTestExecutionReport/MsTestToNUnit.xslt b/Reporting/MsTestExecutionReport/MsTestToNUnit.xslt index 87c931941..a7e088db8 100644 --- a/Reporting/MsTestExecutionReport/MsTestToNUnit.xslt +++ b/Reporting/MsTestExecutionReport/MsTestToNUnit.xslt @@ -1,8 +1,8 @@  - + @@ -33,7 +34,8 @@ - + + @@ -46,7 +48,7 @@ - + @@ -89,12 +91,12 @@ 0.000 0 - + - + @@ -190,7 +192,8 @@ - + + . @@ -252,7 +255,21 @@ - + + + + + + + + + + + + + + + @@ -263,8 +280,8 @@ - + - + diff --git a/Runtime/Assist/EnumerableProjection.cs b/Runtime/Assist/EnumerableProjection.cs index bb188cac8..bbf17f290 100644 --- a/Runtime/Assist/EnumerableProjection.cs +++ b/Runtime/Assist/EnumerableProjection.cs @@ -98,7 +98,7 @@ public void Reset() public class Projection { private readonly T item; - private IEnumerable properties; + private readonly IEnumerable properties; public Projection(T item, IEnumerable properties) { @@ -106,6 +106,16 @@ public Projection(T item, IEnumerable properties) this.properties = properties; } + public T Value + { + get { return item; } + } + + public object this[string key] + { + get { return item.GetMemberValue(key); } + } + public override bool Equals(object obj) { if (obj is Projection) @@ -113,7 +123,7 @@ public override bool Equals(object obj) var otherProjection = obj as Projection; if (item != null && otherProjection.item != null) { - IEnumerable properties = this.properties; + var properties = this.properties; if (otherProjection.properties != null) { if (properties == null) @@ -146,8 +156,8 @@ private static bool Compare(T t1, T t2, IEnumerable properties) if (t1.GetType().GetProperty(property) == null || t2.GetType().GetProperty(property) == null) return false; - var thisValue = t1.GetPropertyValue(property); - var otherValue = t2.GetPropertyValue(property); + var thisValue = t1.GetMemberValue(property); + var otherValue = t2.GetMemberValue(property); if (thisValue != null) { if (otherValue == null) diff --git a/Runtime/Assist/InstanceComparisonExtensionMethods.cs b/Runtime/Assist/InstanceComparisonExtensionMethods.cs index 57f36bb20..aa7769d4b 100644 --- a/Runtime/Assist/InstanceComparisonExtensionMethods.cs +++ b/Runtime/Assist/InstanceComparisonExtensionMethods.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using TechTalk.SpecFlow.Assist.ValueComparers; @@ -50,10 +50,22 @@ private static string DescribeTheErrorForThisDifference(Difference difference) private static IEnumerable FindAnyDifferences(Table table, T instance) { return from row in table.Rows - where ThePropertyDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row) + where TheMemberDoesNotExist(instance, row) || TheValuesDoNotMatch(instance, row) select CreateDifferenceForThisRow(instance, row); } + private static bool TheMemberDoesNotExist(T instance, TableRow row) + { + return ThePropertyDoesNotExist(instance, row) && TheFieldDoesNotExist(instance, row); + } + + private static bool TheFieldDoesNotExist(T instance, TableRow row) + { + return instance.GetType().GetFields() + .Any(property => TEHelpers.IsMemberMatchingToColumnName(property, row.Id())) == false; + } + + private static bool ThereAreAnyDifferences(IEnumerable differences) { return differences.Count() > 0; @@ -68,7 +80,7 @@ private static bool ThePropertyDoesNotExist(T instance, TableRow row) private static bool TheValuesDoNotMatch(T instance, TableRow row) { var expected = GetTheExpectedValue(row); - var propertyValue = instance.GetPropertyValue(row.Id()); + var propertyValue = instance.GetMemberValue(row.Id()); var valueComparers = new IValueComparer[] { @@ -93,19 +105,19 @@ private static string GetTheExpectedValue(TableRow row) private static Difference CreateDifferenceForThisRow(T instance, TableRow row) { - if (ThePropertyDoesNotExist(instance, row)) + if (TheMemberDoesNotExist(instance, row)) return new Difference - { - Property = row.Id(), - DoesNotExist = true - }; + { + Property = row.Id(), + DoesNotExist = true + }; return new Difference - { - Property = row.Id(), - Expected = row.Value(), - Actual = instance.GetPropertyValue(row.Id()) - }; + { + Property = row.Id(), + Expected = row.Value(), + Actual = instance.GetMemberValue(row.Id()) + }; } private class Difference @@ -137,4 +149,5 @@ public ComparisonException(string message) { } } -} \ No newline at end of file +} + diff --git a/Runtime/Assist/MemberExtensionMethods.cs b/Runtime/Assist/MemberExtensionMethods.cs new file mode 100644 index 000000000..d43772a50 --- /dev/null +++ b/Runtime/Assist/MemberExtensionMethods.cs @@ -0,0 +1,53 @@ +using System.Linq; +using System.Reflection; + +namespace TechTalk.SpecFlow.Assist +{ + internal static class MemberExtensionMethods + { + public static object GetMemberValue(this object @object, string memberName) + { + var property = GetThePropertyOnThisObject(@object, memberName); + + if (property != null) + { + return property.GetValue(@object, null); + } + + var field = GetTheFieldOnThisObject(@object, memberName); + return field.GetValue(@object); + + } + + public static void SetMemberValue(this object @object, string propertyName, object value) + { + var property = GetThePropertyOnThisObject(@object, propertyName); + + if (property != null) + { + property.SetValue(@object, value, null); + return; + } + + var field = GetTheFieldOnThisObject(@object, propertyName); + field.SetValue(@object, value); + + } + + + private static FieldInfo GetTheFieldOnThisObject(object @object, string fieldName) + { + var type = @object.GetType(); + return type.GetFields() + .FirstOrDefault(x => TEHelpers.IsMemberMatchingToColumnName(x, fieldName)); + + } + + private static PropertyInfo GetThePropertyOnThisObject(object @object, string propertyName) + { + var type = @object.GetType(); + return type.GetProperties() + .FirstOrDefault(x => TEHelpers.IsMemberMatchingToColumnName(x, propertyName)); + } + } +} \ No newline at end of file diff --git a/Runtime/Assist/ProjectionExtensionMethods.cs b/Runtime/Assist/ProjectionExtensionMethods.cs index f3740a3f7..7da695087 100644 --- a/Runtime/Assist/ProjectionExtensionMethods.cs +++ b/Runtime/Assist/ProjectionExtensionMethods.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; namespace TechTalk.SpecFlow.Assist { @@ -22,6 +24,45 @@ public static IEnumerable> ToProjectionOfSet(this Table table, public static IEnumerable> ToProjectionOfInstance(this Table table, T instance) { return new EnumerableProjection(table); - } + } + + public static void CompareToProjectionOfSet( + this Table table, + IEnumerable query, + Func>, IEnumerable>, bool> compare) + { + var tableProjection = table.ToProjectionOfSet(query); + var queryProjection = query.ToProjection(); + if (!compare(tableProjection, queryProjection)) + { + throw new ComparisonException(@"Set projections do not match"); + } + } + + public static void CompareToProjectionOfSet( + this Table table, + IEnumerable query, + Func>, IEnumerable>, IEnumerable>> diff, + string identProperty) + { + var tableProjection = table.ToProjectionOfSet(query); + var queryProjection = query.ToProjection(); + var setDifference = diff(tableProjection, queryProjection); + if (setDifference.Any()) + { + var idents = string.Join(",", setDifference.Select(x => x[identProperty].ToString()).ToArray()); + throw new ComparisonException(string.Format(@"The following row projections do not match: {0}", idents)); + } + } + + public static void CompareToProjectionOfInstance(this Table table, T instance) + { + var tableProjection = table.ToProjectionOfInstance(instance); + var instanceProjection = (new List() {instance}).ToProjection(); + if (!tableProjection.Equals(instanceProjection)) + { + throw new ComparisonException(@"Instance projections do not match"); + } + } } -} \ No newline at end of file +} diff --git a/Runtime/Assist/PropertyExtensionMethods.cs b/Runtime/Assist/PropertyExtensionMethods.cs deleted file mode 100644 index 966b7a602..000000000 --- a/Runtime/Assist/PropertyExtensionMethods.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Linq; -using System.Reflection; - -namespace TechTalk.SpecFlow.Assist -{ - internal static class PropertyExtensionMethods - { - public static object GetPropertyValue(this object @object, string propertyName) - { - var property = GetThePropertyOnThisObject(@object, propertyName); - return property.GetValue(@object, null); - } - - public static void SetPropertyValue(this object @object, string propertyName, object value) - { - var property = GetThePropertyOnThisObject(@object, propertyName); - property.SetValue(@object, value, null); - } - - private static PropertyInfo GetThePropertyOnThisObject(object @object, string propertyName) - { - var type = @object.GetType(); - return type.GetProperties() - .FirstOrDefault(x => TEHelpers.IsMemberMatchingToColumnName(x, propertyName)); - } - } -} diff --git a/Runtime/Assist/SetComparer.cs b/Runtime/Assist/SetComparer.cs index 8e2dc9e70..2a9f7753f 100644 --- a/Runtime/Assist/SetComparer.cs +++ b/Runtime/Assist/SetComparer.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace TechTalk.SpecFlow.Assist { @@ -141,14 +142,20 @@ private static List GetTheActualItems(IEnumerable set) private void AssertThatAllColumnsInTheTableMatchToPropertiesOnTheType() { - var propertiesThatDoNotExist = from columnHeader in table.Header - where (typeof (T).GetProperties().Any(property => TEHelpers.IsMemberMatchingToColumnName(property, columnHeader)) == false) - select columnHeader; + var type = typeof(T); + var memberList = new List(); + memberList.AddRange(type.GetProperties()); + memberList.AddRange(type.GetFields()); - if (propertiesThatDoNotExist.Any()) + var membersThatDontExist = from columnHeader in table.Header + where (memberList.Any(member => TEHelpers.IsMemberMatchingToColumnName(member, columnHeader)) == false) + select columnHeader; + + if (membersThatDontExist.Any()) throw new ComparisonException( - propertiesThatDoNotExist.Aggregate(@"The following fields do not exist:", + membersThatDontExist.Aggregate(@"The following fields do not exist:", (running, next) => running + string.Format("{0}{1}", Environment.NewLine, next))); } + } } \ No newline at end of file diff --git a/Runtime/Assist/TEHelpers.cs b/Runtime/Assist/TEHelpers.cs index 95c31a257..3724da4e1 100644 --- a/Runtime/Assist/TEHelpers.cs +++ b/Runtime/Assist/TEHelpers.cs @@ -65,8 +65,12 @@ internal static bool IsMemberMatchingToColumnName(MemberInfo member, string colu internal static bool MatchesThisColumnName(this string propertyName, string columnName) { - return propertyName.Equals(columnName.Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase); + var cleanedColumnName = columnName + .Replace(" ", string.Empty) + .Replace("-", string.Empty) + .Replace("?", string.Empty); + return propertyName.Equals(cleanedColumnName, StringComparison.OrdinalIgnoreCase); } internal static void LoadInstanceWithKeyValuePairs(Table table, object instance) @@ -147,10 +151,23 @@ internal static Dictionary> GetTypeHandlersForField {typeof (DateTime?), (TableRow row) => new NullableDateTimeValueRetriever(v => new DateTimeValueRetriever().GetValue(v)).GetValue(row[1])}, {typeof (Guid), (TableRow row) => new GuidValueRetriever().GetValue(row[1])}, {typeof (Guid?), (TableRow row) => new NullableGuidValueRetriever(v => new GuidValueRetriever().GetValue(v)).GetValue(row[1])}, - {typeof (Enum), (TableRow row) => new EnumValueRetriever().GetValue(row[1], type.GetProperties().First(x => x.Name.MatchesThisColumnName(row[0])).PropertyType)}, + {typeof (Enum), (TableRow row) => new EnumValueRetriever().GetValue(row[1], GetMemberType(type, row))}, }; } + private static Type GetMemberType(Type type, TableRow row) + { + // search for property matching on name + if (type.GetProperties().Any(x => x.Name.MatchesThisColumnName(row[0]))) + { + return type.GetProperties().First(x => x.Name.MatchesThisColumnName(row[0])).PropertyType; + } + + //search for matching field + return type.GetFields().First(x => x.Name.MatchesThisColumnName(row[0])).FieldType; + + } + internal class MemberHandler { public TableRow Row { get; set; } @@ -190,4 +207,4 @@ private static bool TheFirstRowValueIsTheNameOfAProperty(Table table, Type type) .Any(property => IsMemberMatchingToColumnName(property, firstRowValue)); } } -} \ No newline at end of file +} diff --git a/Runtime/Assist/TableDiffExceptionBuilder.cs b/Runtime/Assist/TableDiffExceptionBuilder.cs index f0f64e68a..14e13466b 100644 --- a/Runtime/Assist/TableDiffExceptionBuilder.cs +++ b/Runtime/Assist/TableDiffExceptionBuilder.cs @@ -29,7 +29,7 @@ public string GetTheTableDiffExceptionMessage(TableDifferenceResults tableDif { var line = "+ |"; foreach (var header in tableDifferenceResults.Table.Header) - line += string.Format(" {0} |", item.GetPropertyValue(header)); + line += string.Format(" {0} |", item.GetMemberValue(header)); realData.AppendLine(line); } diff --git a/Runtime/Assist/TableExtensionMethods.cs b/Runtime/Assist/TableExtensionMethods.cs index d74e5214b..c581ae141 100644 --- a/Runtime/Assist/TableExtensionMethods.cs +++ b/Runtime/Assist/TableExtensionMethods.cs @@ -55,5 +55,20 @@ public static IEnumerable CreateSet(this Table table, Func methodToCrea return list; } + + public static IEnumerable CreateSet(this Table table, Func methodToCreateEachInstance) + { + var list = new List(); + + var pivotTable = new PivotTable(table); + for (var index = 0; index < table.Rows.Count(); index++) + { + var instance = methodToCreateEachInstance(table.Rows[index]); + pivotTable.GetInstanceTable(index).FillInstance(instance); + list.Add(instance); + } + + return list; + } } } \ No newline at end of file diff --git a/Runtime/Assist/ValueRetrievers/EnumValueRetriever.cs b/Runtime/Assist/ValueRetrievers/EnumValueRetriever.cs index f4e054d4d..d4b7407a8 100644 --- a/Runtime/Assist/ValueRetrievers/EnumValueRetriever.cs +++ b/Runtime/Assist/ValueRetrievers/EnumValueRetriever.cs @@ -13,6 +13,9 @@ public object GetValue(string value, Type enumType) private object ConvertTheStringToAnEnum(string value, Type enumType) { + if (!ThisIsNotANullableEnum(enumType) && string.IsNullOrEmpty(value)) + return null; + return Enum.Parse(GetTheEnumType(enumType), ParseTheValue(value), true); } diff --git a/Runtime/Bindings/BindingInvoker.cs b/Runtime/Bindings/BindingInvoker.cs index ff93dabce..2f7113520 100644 --- a/Runtime/Bindings/BindingInvoker.cs +++ b/Runtime/Bindings/BindingInvoker.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Threading.Tasks; using TechTalk.SpecFlow.Bindings.Reflection; using TechTalk.SpecFlow.Compatibility; using TechTalk.SpecFlow.Configuration; @@ -43,6 +44,12 @@ public object InvokeBinding(IBinding binding, IContextManager contextManager, ob Array.Copy(arguments, 0, invokeArgs, 1, arguments.Length); invokeArgs[0] = contextManager; result = bindingAction.DynamicInvoke(invokeArgs); + + if (result is Task) + { + ((Task) result).Wait(); + } + stopwatch.Stop(); } @@ -64,6 +71,12 @@ public object InvokeBinding(IBinding binding, IContextManager contextManager, ob ex = ex.PreserveStackTrace(errorProvider.GetMethodText(binding.Method)); throw ex; } + catch (AggregateException agEx) //from Task.Wait(); + { + var ex = agEx.InnerExceptions.First(); + ex = ex.PreserveStackTrace(errorProvider.GetMethodText(binding.Method)); + throw ex; + } } private CultureInfoScope CreateCultureInfoScope(IContextManager contextManager) diff --git a/Runtime/Bindings/HorizontalTableConverter.cs b/Runtime/Bindings/HorizontalTableConverter.cs new file mode 100644 index 000000000..6278c9f89 --- /dev/null +++ b/Runtime/Bindings/HorizontalTableConverter.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using TechTalk.SpecFlow.Bindings.Reflection; +using System.Globalization; +using TechTalk.SpecFlow.Assist; + +namespace TechTalk.SpecFlow.Bindings +{ + internal class HorizontalTableConverter : TableConverterBase + { + private readonly IStepArgumentTypeConverter stepArgumentTypeConverter; + + public HorizontalTableConverter(IStepArgumentTypeConverter stepArgumentTypeConverter) + { + this.stepArgumentTypeConverter = stepArgumentTypeConverter; + } + + protected override object Convert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var itemRuntimeType = GetItemRuntimeType(typeToConvertTo); + var rowFetcher = GetValidRowFetcherDelegate(table, itemRuntimeType, cultureInfo); + var result = Array.CreateInstance(itemRuntimeType.Type, table.RowCount); + + for (int i = 0; i < result.Length; i++) + { + var item = stepArgumentTypeConverter.Convert(rowFetcher(i), itemRuntimeType, cultureInfo); + result.SetValue(item, i); + } + + return result; + } + + protected override bool CanConvert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var itemRuntimeType = GetItemRuntimeType(typeToConvertTo); + if (itemRuntimeType == null || IsVerticalTable(table, itemRuntimeType)) + return false; + + var rowFetcher = GetValidRowFetcherDelegate(table, itemRuntimeType, cultureInfo); + return rowFetcher != null; + } + + private RuntimeBindingType GetItemRuntimeType(IBindingType typeToConvertTo) + { + var runtimeType = typeToConvertTo as RuntimeBindingType; + if (runtimeType == null) + return null; + + var type = runtimeType.Type; + + if (type.IsArray && type.GetArrayRank() == 1) + return new RuntimeBindingType(type.GetElementType()); + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + return new RuntimeBindingType(type.GetGenericArguments()[0]); + + return null; + } + + private Func GetValidRowFetcherDelegate(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + if (table.RowCount == 0) + return x => null; + + var pivotTable = new PivotTable(table); + if (stepArgumentTypeConverter.CanConvert(pivotTable.GetInstanceTable(0), typeToConvertTo, cultureInfo)) + return x => pivotTable.GetInstanceTable(x); + + if (table.Header.Count == 1 && + stepArgumentTypeConverter.CanConvert(table.Rows[0][0], typeToConvertTo, cultureInfo)) + return x => table.Rows[x][0]; + + return null; + } + } +} diff --git a/Runtime/Bindings/IndentityConverter.cs b/Runtime/Bindings/IndentityConverter.cs new file mode 100644 index 000000000..08d6ed90e --- /dev/null +++ b/Runtime/Bindings/IndentityConverter.cs @@ -0,0 +1,18 @@ +using System.Globalization; +using TechTalk.SpecFlow.Bindings.Reflection; + +namespace TechTalk.SpecFlow.Bindings +{ + internal class IndentityConverter : IStepArgumentTypeConverter + { + public object Convert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + return value; + } + + public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + return typeToConvertTo.IsAssignableTo(value.GetType()); + } + } +} diff --git a/Runtime/Bindings/SimpleConverter.cs b/Runtime/Bindings/SimpleConverter.cs new file mode 100644 index 000000000..049c119f7 --- /dev/null +++ b/Runtime/Bindings/SimpleConverter.cs @@ -0,0 +1,54 @@ +using System; +using System.Globalization; +using TechTalk.SpecFlow.Assist.ValueRetrievers; +using TechTalk.SpecFlow.Bindings.Reflection; + +namespace TechTalk.SpecFlow.Bindings +{ + internal class SimpleConverter : IStepArgumentTypeConverter + { + public object Convert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var runtimeType = (RuntimeBindingType)typeToConvertTo; + + if (runtimeType.Type.IsEnum && value is string) + return Enum.Parse(runtimeType.Type, ((string)value).Replace(" ", ""), true); + + if (runtimeType.Type == typeof(Guid?) && string.IsNullOrEmpty(value as string)) + return null; + + if (runtimeType.Type == typeof(Guid) || runtimeType.Type == typeof(Guid?)) + return new GuidValueRetriever().GetValue(value as string); + + return System.Convert.ChangeType(value, runtimeType.Type, cultureInfo); + } + + public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + if (!(typeToConvertTo is RuntimeBindingType)) + return false; + + try + { + Convert(value, typeToConvertTo, cultureInfo); + return true; + } + catch (InvalidCastException) + { + return false; + } + catch (OverflowException) + { + return false; + } + catch (FormatException) + { + return false; + } + catch (ArgumentException) + { + return false; + } + } + } +} diff --git a/Runtime/Bindings/StepArgumentTransformationConverter.cs b/Runtime/Bindings/StepArgumentTransformationConverter.cs new file mode 100644 index 000000000..1d8dafb65 --- /dev/null +++ b/Runtime/Bindings/StepArgumentTransformationConverter.cs @@ -0,0 +1,90 @@ +using System; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using TechTalk.SpecFlow.Bindings.Reflection; +using TechTalk.SpecFlow.Infrastructure; +using TechTalk.SpecFlow.Tracing; + +namespace TechTalk.SpecFlow.Bindings +{ + internal class StepArgumentTransformationConverter : IStepArgumentTypeConverter + { + private readonly IStepArgumentTypeConverter stepArgumentTypeConverter; + private readonly ITestTracer testTracer; + private readonly IBindingRegistry bindingRegistry; + private readonly IContextManager contextManager; + private readonly IBindingInvoker bindingInvoker; + + public StepArgumentTransformationConverter(IStepArgumentTypeConverter stepArgumentTypeConverter, ITestTracer testTracer, IBindingRegistry bindingRegistry, IContextManager contextManager, IBindingInvoker bindingInvoker) + { + this.stepArgumentTypeConverter = stepArgumentTypeConverter; + this.testTracer = testTracer; + this.bindingRegistry = bindingRegistry; + this.contextManager = contextManager; + this.bindingInvoker = bindingInvoker; + } + + public object Convert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var stepTransformation = GetMatchingStepTransformation(value, typeToConvertTo, true); + + if (stepTransformation == null) + throw new SpecFlowException("The StepTransformationConverter cannot convert the specified value."); + + return DoTransform(stepTransformation, value, cultureInfo); + } + + public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + return GetMatchingStepTransformation(value, typeToConvertTo, false) != null; + } + + private IStepArgumentTransformationBinding GetMatchingStepTransformation(object value, IBindingType typeToConvertTo, bool traceWarning) + { + var stepTransformations = bindingRegistry.GetStepTransformations().Where(t => CanConvert(t, value, typeToConvertTo)).ToArray(); + + if (stepTransformations.Length > 1 && traceWarning) + { + testTracer.TraceWarning(string.Format("Multiple step transformation matches to the input ({0}, target type: {1}). We use the first.", value, typeToConvertTo)); + } + + return stepTransformations.FirstOrDefault(); + } + + private bool CanConvert(IStepArgumentTransformationBinding stepTransformationBinding, object value, IBindingType typeToConvertTo) + { + if (!stepTransformationBinding.Method.ReturnType.TypeEquals(typeToConvertTo)) + return false; + + if (stepTransformationBinding.Regex != null) + { + return value is string && stepTransformationBinding.Regex.IsMatch((string)value); + } + + return stepTransformationBinding.Method.Parameters.Count() == 1; + } + + private object DoTransform(IStepArgumentTransformationBinding stepTransformation, object value, CultureInfo cultureInfo) + { + object[] arguments; + if (stepTransformation.Regex != null && value is string) + arguments = GetStepTransformationArgumentsFromRegex(stepTransformation, (string)value, cultureInfo); + else + arguments = new object[] { value }; + + TimeSpan duration; + return bindingInvoker.InvokeBinding(stepTransformation, contextManager, arguments, testTracer, out duration); + } + + private object[] GetStepTransformationArgumentsFromRegex(IStepArgumentTransformationBinding stepTransformation, string stepSnippet, CultureInfo cultureInfo) + { + var match = stepTransformation.Regex.Match(stepSnippet); + var argumentStrings = match.Groups.Cast().Skip(1).Select(g => g.Value); + var bindingParameters = stepTransformation.Method.Parameters.ToArray(); + return argumentStrings + .Select((arg, argIndex) => stepArgumentTypeConverter.Convert(arg, bindingParameters[argIndex].Type, cultureInfo)) + .ToArray(); + } + } +} diff --git a/Runtime/Bindings/StepArgumentTypeConverter.cs b/Runtime/Bindings/StepArgumentTypeConverter.cs index e603e1cc2..067cd9f14 100644 --- a/Runtime/Bindings/StepArgumentTypeConverter.cs +++ b/Runtime/Bindings/StepArgumentTypeConverter.cs @@ -1,11 +1,9 @@ -using System; using System.Globalization; using System.Linq; -using System.Text.RegularExpressions; -using TechTalk.SpecFlow.Assist.ValueRetrievers; using TechTalk.SpecFlow.Bindings.Reflection; using TechTalk.SpecFlow.Infrastructure; using TechTalk.SpecFlow.Tracing; +using System.Collections.Generic; namespace TechTalk.SpecFlow.Bindings { @@ -17,135 +15,34 @@ public interface IStepArgumentTypeConverter public class StepArgumentTypeConverter : IStepArgumentTypeConverter { - private readonly ITestTracer testTracer; - private readonly IBindingRegistry bindingRegistry; - private readonly IContextManager contextManager; - private readonly IBindingInvoker bindingInvoker; + private readonly IEnumerable converters; public StepArgumentTypeConverter(ITestTracer testTracer, IBindingRegistry bindingRegistry, IContextManager contextManager, IBindingInvoker bindingInvoker) { - this.testTracer = testTracer; - this.bindingRegistry = bindingRegistry; - this.contextManager = contextManager; - this.bindingInvoker = bindingInvoker; - } - - protected virtual IStepArgumentTransformationBinding GetMatchingStepTransformation(object value, IBindingType typeToConvertTo, bool traceWarning) - { - var stepTransformations = bindingRegistry.GetStepTransformations().Where(t => CanConvert(t, value, typeToConvertTo)).ToArray(); - if (stepTransformations.Length > 1 && traceWarning) + converters = new IStepArgumentTypeConverter[] { - testTracer.TraceWarning(string.Format("Multiple step transformation matches to the input ({0}, target type: {1}). We use the first.", value, typeToConvertTo)); - } - - return stepTransformations.Length > 0 ? stepTransformations[0] : null; + new IndentityConverter(), + new StepArgumentTransformationConverter(this, testTracer, bindingRegistry, contextManager, bindingInvoker), + new VerticalTableConverter(this), + new HorizontalTableConverter(this), + new SimpleConverter() + }; } public object Convert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) { - if (value == null) throw new ArgumentNullException("value"); - - if (typeToConvertTo == value.GetType()) - return value; - - var stepTransformation = GetMatchingStepTransformation(value, typeToConvertTo, true); - if (stepTransformation != null) - return DoTransform(stepTransformation, value, cultureInfo); - - return ConvertSimple(typeToConvertTo, value, cultureInfo); - } - - private object DoTransform(IStepArgumentTransformationBinding stepTransformation, object value, CultureInfo cultureInfo) - { - object[] arguments; - if (stepTransformation.Regex != null && value is string) - arguments = GetStepTransformationArgumentsFromRegex(stepTransformation, (string)value, cultureInfo); - else - arguments = new object[] {value}; - - TimeSpan duration; - return bindingInvoker.InvokeBinding(stepTransformation, contextManager, arguments, testTracer, out duration); - } - - private object[] GetStepTransformationArgumentsFromRegex(IStepArgumentTransformationBinding stepTransformation, string stepSnippet, CultureInfo cultureInfo) - { - var match = stepTransformation.Regex.Match(stepSnippet); - var argumentStrings = match.Groups.Cast().Skip(1).Select(g => g.Value); - var bindingParameters = stepTransformation.Method.Parameters.ToArray(); - return argumentStrings - .Select((arg, argIndex) => this.Convert(arg, bindingParameters[argIndex].Type, cultureInfo)) - .ToArray(); + var converter = GetConverter(value, typeToConvertTo, cultureInfo); + return converter.Convert(value, typeToConvertTo, cultureInfo); } public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) { - if (value == null) throw new ArgumentNullException("value"); - - if (typeToConvertTo == value.GetType()) - return true; - - var stepTransformation = GetMatchingStepTransformation(value, typeToConvertTo, false); - if (stepTransformation != null) - return true; - - return CanConvertSimple(typeToConvertTo, value, cultureInfo); - } - - private bool CanConvert(IStepArgumentTransformationBinding stepTransformationBinding, object value, IBindingType typeToConvertTo) - { - if (!stepTransformationBinding.Method.ReturnType.TypeEquals(typeToConvertTo)) - return false; - - if (stepTransformationBinding.Regex != null && value is string) - return stepTransformationBinding.Regex.IsMatch((string) value); - return true; - } - - private static object ConvertSimple(IBindingType typeToConvertTo, object value, CultureInfo cultureInfo) - { - if (!(typeToConvertTo is RuntimeBindingType)) - throw new SpecFlowException("The StepArgumentTypeConverter can be used with runtime types only."); - - return ConvertSimple(((RuntimeBindingType) typeToConvertTo).Type, value, cultureInfo); - } - - private static object ConvertSimple(Type typeToConvertTo, object value, CultureInfo cultureInfo) - { - if (typeToConvertTo.IsEnum && value is string) - return Enum.Parse(typeToConvertTo, (string)value, true); - - if (typeToConvertTo == typeof(Guid?) && string.IsNullOrEmpty(value as string)) - return null; - - if (typeToConvertTo == typeof(Guid) || typeToConvertTo == typeof(Guid?)) - return new GuidValueRetriever().GetValue(value as string); - - return System.Convert.ChangeType(value, typeToConvertTo, cultureInfo); + return GetConverter(value, typeToConvertTo, cultureInfo) != null; } - public static bool CanConvertSimple(IBindingType typeToConvertTo, object value, CultureInfo cultureInfo) + private IStepArgumentTypeConverter GetConverter(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) { - try - { - ConvertSimple(typeToConvertTo, value, cultureInfo); - return true; - } - catch (InvalidCastException) - { - return false; - } - catch (OverflowException) - { - return false; - } - catch (FormatException) - { - return false; - } - catch (ArgumentException) - { - return false; - } + return converters.FirstOrDefault(x => x.CanConvert(value, typeToConvertTo, cultureInfo)); } } } diff --git a/Runtime/Bindings/TableConverterBase.cs b/Runtime/Bindings/TableConverterBase.cs new file mode 100644 index 000000000..d83d40f72 --- /dev/null +++ b/Runtime/Bindings/TableConverterBase.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TechTalk.SpecFlow.Bindings.Reflection; +using System.Globalization; +using System.Reflection; + +namespace TechTalk.SpecFlow.Bindings +{ + internal abstract class TableConverterBase : IStepArgumentTypeConverter + { + protected abstract object Convert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo); + + protected abstract bool CanConvert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo); + + public object Convert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + return Convert((Table)value, typeToConvertTo, cultureInfo); + } + + public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var table = value as Table; + if (table == null) + return false; + + return CanConvert(table, typeToConvertTo, cultureInfo); + } + + protected static bool IsVerticalTable(Table table, RuntimeBindingType runtimeBindingType) + { + if (table.Header.Count != 2) + return false; + + if (table.RowCount == 0) + return true; + + var writableProperties = GetWritableProperties(runtimeBindingType.Type); + return writableProperties.ContainsKey(SanitizePropertyName(table.Rows[0][0])); + } + + protected static Dictionary GetWritableProperties(Type type) + { + return type.GetProperties().Where(x => x.CanWrite).ToDictionary(x => SanitizePropertyName(x.Name)); + } + + protected static string SanitizePropertyName(string name) + { + return name.Replace(" ", "").ToLowerInvariant(); + } + } +} diff --git a/Runtime/Bindings/VerticalTableConverter.cs b/Runtime/Bindings/VerticalTableConverter.cs new file mode 100644 index 000000000..d97bc31eb --- /dev/null +++ b/Runtime/Bindings/VerticalTableConverter.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TechTalk.SpecFlow.Bindings.Reflection; +using System.Globalization; +using System.Reflection; +using TechTalk.SpecFlow.Assist; + +namespace TechTalk.SpecFlow.Bindings +{ + internal class VerticalTableConverter : TableConverterBase + { + private readonly IStepArgumentTypeConverter stepArgumentTypeConverter; + + public VerticalTableConverter(IStepArgumentTypeConverter stepArgumentTypeConverter) + { + this.stepArgumentTypeConverter = stepArgumentTypeConverter; + } + + protected override object Convert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var runtimeBindingType = (RuntimeBindingType)typeToConvertTo; + var verticalTable = GetVerticalTable(table, runtimeBindingType); + var values = GetValues(verticalTable); + var objectInitializationData = GetObjectInitializationData(values.Keys, runtimeBindingType.Type); + var bindingTypes = objectInitializationData.GetBindingTypes(); + + var convertedValues = values + .Where(x => bindingTypes.ContainsKey(x.Key)) + .ToDictionary( + x => x.Key, + x => stepArgumentTypeConverter.Convert(x.Value, bindingTypes[x.Key], cultureInfo)); + var constructorParameters = objectInitializationData.Constructor.GetParameters() + .Select(x => convertedValues[SanitizePropertyName(x.Name)]).ToArray(); + var result = objectInitializationData.Constructor.Invoke(constructorParameters); + + foreach (var property in objectInitializationData.PropertiesToSet) + { + var value = convertedValues[SanitizePropertyName(property.Name)]; + property.SetValue(result, value, null); + } + + return result; + } + + protected override bool CanConvert(Table table, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + var runtimeBindingType = typeToConvertTo as RuntimeBindingType; + if (runtimeBindingType == null) + return false; + + var verticalTable = GetVerticalTable(table, runtimeBindingType); + if (verticalTable == null) + return false; + + var values = GetValues(verticalTable); + var objectInitializationData = GetObjectInitializationData(values.Keys, runtimeBindingType.Type); + if (objectInitializationData == null) + return false; + + var bindingTypes = objectInitializationData.GetBindingTypes(); + return values + .Where(x => bindingTypes.ContainsKey(x.Key)) + .All(x => stepArgumentTypeConverter.CanConvert(x.Value, bindingTypes[x.Key], cultureInfo)); + } + + private Table GetVerticalTable(Table table, RuntimeBindingType runtimeBindingType) + { + if (IsVerticalTable(table, runtimeBindingType)) + return table; + + if (table.Rows.Count == 1) + { + var pivotedTable = new PivotTable(table).GetInstanceTable(0); + + if (IsVerticalTable(pivotedTable, runtimeBindingType)) + return pivotedTable; + } + + return null; + } + + private Dictionary GetValues(Table table) + { + return table.Rows.ToDictionary(x => SanitizePropertyName(x[0]), x => x[1]); + } + + private ObjectInitializationData GetObjectInitializationData(IEnumerable columns, Type type) + { + var writableProperties = GetWritableProperties(type); + var requiredParameters = new HashSet(columns.Where(x => !writableProperties.ContainsKey(x))); + var optionalParameters = new HashSet(columns.Where(writableProperties.ContainsKey)); + + var constructor = GetBestMatchingConstructor(type, requiredParameters, optionalParameters); + if (constructor == null) + return null; + + var constructorColumns = new HashSet(constructor.GetParameters().Select(x => SanitizePropertyName(x.Name))); + var propertiesToSet = columns + .Where(x => !constructorColumns.Contains(x) && writableProperties.ContainsKey(x)) + .Select(x => writableProperties[x]).ToList(); + + return new ObjectInitializationData + { + Constructor = constructor, + PropertiesToSet = propertiesToSet + }; + } + + private ConstructorInfo GetBestMatchingConstructor(Type type, HashSet requiredParameters, HashSet optionalParameters) + { + return type.GetConstructors() + .Select(c => new { Constructor = c, Parameters = c.GetParameters().Select(p => SanitizePropertyName(p.Name)).ToList() }) + .OrderByDescending(x => x.Parameters.Count(requiredParameters.Contains)) + .ThenBy(x => x.Parameters.Count) + .Where(x => x.Parameters.All(p => requiredParameters.Contains(p) || optionalParameters.Contains(p))) + .Select(x => x.Constructor) + .FirstOrDefault(); + } + + private class ObjectInitializationData + { + public ConstructorInfo Constructor { get; set; } + + public List PropertiesToSet { get; set; } + + public Dictionary GetBindingTypes() + { + var bindingTypes = new Dictionary(); + foreach (var parameter in Constructor.GetParameters()) + { + bindingTypes.Add(SanitizePropertyName(parameter.Name), new RuntimeBindingType(parameter.ParameterType)); + } + foreach (var property in PropertiesToSet) + { + bindingTypes.Add(SanitizePropertyName(property.Name), new RuntimeBindingType(property.PropertyType)); + } + return bindingTypes; + } + } + } +} diff --git a/Runtime/TechTalk.SpecFlow.csproj b/Runtime/TechTalk.SpecFlow.csproj index 590300157..3f5066a78 100644 --- a/Runtime/TechTalk.SpecFlow.csproj +++ b/Runtime/TechTalk.SpecFlow.csproj @@ -10,10 +10,11 @@ Properties TechTalk.SpecFlow TechTalk.SpecFlow - v3.5 + v4.0 512 true ..\specflow.snk + true @@ -65,7 +66,7 @@ - + @@ -128,10 +129,12 @@ + + @@ -148,10 +151,14 @@ + + + + diff --git a/Tests/RuntimeTests/AssistTests/InstanceComparisonExtensionMethodsTests.cs b/Tests/RuntimeTests/AssistTests/InstanceComparisonExtensionMethodsTests.cs index e458c4d16..f8d4090ea 100644 --- a/Tests/RuntimeTests/AssistTests/InstanceComparisonExtensionMethodsTests.cs +++ b/Tests/RuntimeTests/AssistTests/InstanceComparisonExtensionMethodsTests.cs @@ -17,6 +17,45 @@ public void SetUp() Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); } + [Test] + public void Does_not_throw_exception_when_value_of_matching_string_field_matches() + { + var table = new Table("Field", "Value"); + table.AddRow("StringField", "Howard Roark"); + + var test = new InstanceComparisonTestObjectWithFields { StringField = "Howard Roark" }; + + ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, test); + + comparisonResult.ExceptionWasThrown.ShouldBeFalse(comparisonResult.ExceptionMessage); + } + + [Test] + public void Throws_exception_when_field_matching_name_does_not_exist() + { + var table = new Table("Field", "Value"); + table.AddRow("DecimalField", "3.5"); + + var test = new InstanceComparisonTestObjectWithFields(); + + ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, test); + + comparisonResult.ExceptionWasThrown.ShouldBeTrue(); + } + + [Test] + public void Throws_exception_when_field_matches_on_name_but_not_on_value() + { + var table = new Table("Field", "Value"); + table.AddRow("IntField", "3"); + + var test = new InstanceComparisonTestObjectWithFields { IntField = 5 }; + + ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, test); + + comparisonResult.ExceptionWasThrown.ShouldBeTrue(); + } + [Test] public void Throws_exception_when_value_of_matching_string_property_does_not_match() { @@ -51,10 +90,10 @@ public void Throws_exception_when_first_row_matches_but_second_does_not() table.AddRow("IntProperty", "20"); var test = new InstanceComparisonTestObject - { - StringProperty = "Howard Roark", - IntProperty = 10 - }; + { + StringProperty = "Howard Roark", + IntProperty = 10 + }; ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, test); @@ -136,10 +175,10 @@ public void Exception_returns_an_exception_for_two_errors_when_there_are_two_dif table.AddRow("IntProperty", "1"); var test = new InstanceComparisonTestObject - { - StringProperty = "Peter Keating", - IntProperty = 2 - }; + { + StringProperty = "Peter Keating", + IntProperty = 2 + }; var exception = GetExceptionThrownByThisComparison(table, test); @@ -170,16 +209,16 @@ public void Will_property_handle_true_boolean_matches() table.AddRow("BoolProperty", "true"); ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, new InstanceComparisonTestObject - { - BoolProperty = true - }); + { + BoolProperty = true + }); comparisonResult.ExceptionWasThrown.ShouldBeFalse(comparisonResult.ExceptionMessage); comparisonResult = ExceptionWasThrownByThisComparison(table, new InstanceComparisonTestObject - { - BoolProperty = false - }); + { + BoolProperty = false + }); comparisonResult.ExceptionWasThrown.ShouldBeTrue(comparisonResult.ExceptionMessage); } @@ -191,9 +230,9 @@ public void Will_match_guids_without_case_insensitivity() table.AddRow("GuidProperty", "DFFC3F4E-670A-400A-8212-C6841E2EA055"); ComparisonTestResult comparisonResult = ExceptionWasThrownByThisComparison(table, new InstanceComparisonTestObject - { - GuidProperty = new Guid("DFFC3F4E-670A-400A-8212-C6841E2EA055") - }); + { + GuidProperty = new Guid("DFFC3F4E-670A-400A-8212-C6841E2EA055") + }); comparisonResult.ExceptionWasThrown.ShouldBeFalse(comparisonResult.ExceptionMessage); } @@ -204,9 +243,9 @@ public void Will_match_decimals_regardless_of_trailing_zeroes() var table = new Table("Field", "Value"); table.AddRow("DecimalProperty", "4.23"); var comparisonResult = ExceptionWasThrownByThisComparison(table, new InstanceComparisonTestObject - { - DecimalProperty = 4.23000000M - }); + { + DecimalProperty = 4.23000000M + }); comparisonResult.ExceptionWasThrown.ShouldBeFalse(); } @@ -264,12 +303,12 @@ public void Can_compare_a_horizontal_table() table.AddRow("Test", "42", "23.01", "11.56"); var test = new InstanceComparisonTestObject - { - StringProperty = "Test", - IntProperty = 42, - DecimalProperty = 23.01M, - FloatProperty = 11.56F - }; + { + StringProperty = "Test", + IntProperty = 42, + DecimalProperty = 23.01M, + FloatProperty = 11.56F + }; var comparisonResult = ExceptionWasThrownByThisComparison(table, test); comparisonResult.ExceptionWasThrown.ShouldBeFalse(comparisonResult.ExceptionMessage); @@ -356,35 +395,7 @@ public void Supports_all_standard_types() comparisonResult.ExceptionWasThrown.ShouldBeFalse(comparisonResult.ExceptionMessage); } - private static ComparisonException GetExceptionThrownByThisComparison(Table table, InstanceComparisonTestObject test) - { - try - { - table.CompareToInstance(test); - } - catch (ComparisonException ex) - { - return ex; - } - return null; - } - - private static ComparisonTestResult ExceptionWasThrownByThisComparison(Table table, InstanceComparisonTestObject test) - { - var result = new ComparisonTestResult { ExceptionWasThrown = false }; - try - { - table.CompareToInstance(test); - } - catch (ComparisonException ex) - { - result.ExceptionWasThrown = true; - result.ExceptionMessage = ex.Message; - } - return result; - } - - private static ComparisonException GetExceptionThrownByThisComparison(Table table, StandardTypesComparisonTestObject test) + private static ComparisonException GetExceptionThrownByThisComparison(Table table, object test) { try { @@ -397,7 +408,7 @@ private static ComparisonException GetExceptionThrownByThisComparison(Table tabl return null; } - private static ComparisonTestResult ExceptionWasThrownByThisComparison(Table table, StandardTypesComparisonTestObject test) + private static ComparisonTestResult ExceptionWasThrownByThisComparison(Table table, object test) { var result = new ComparisonTestResult { ExceptionWasThrown = false }; try diff --git a/Tests/RuntimeTests/AssistTests/ProjectionTests.cs b/Tests/RuntimeTests/AssistTests/ProjectionTests.cs index 8ba3ba8aa..7a13a534d 100644 --- a/Tests/RuntimeTests/AssistTests/ProjectionTests.cs +++ b/Tests/RuntimeTests/AssistTests/ProjectionTests.cs @@ -1,208 +1,255 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using NUnit.Framework; -using TechTalk.SpecFlow.Assist; -using TechTalk.SpecFlow.RuntimeTests.AssistTests.TestInfrastructure; - -namespace TechTalk.SpecFlow.RuntimeTests.AssistTests -{ - [TestFixture] - public class ProjectionTests - { - private SetComparisonTestObject testInstance; - private IEnumerable testCollection; - private Guid testGuid1 = Guid.NewGuid(); - private Guid testGuid2 = Guid.NewGuid(); - - [SetUp] - public void SetUp() - { - Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); - - testInstance = new SetComparisonTestObject - { - DateTimeProperty = DateTime.Today, - GuidProperty = testGuid1, - IntProperty = 1, - StringProperty = "a" - }; - testCollection = new[] - { - testInstance, - new SetComparisonTestObject - { - DateTimeProperty = DateTime.Today, - GuidProperty = testGuid2, - IntProperty = 2, - StringProperty = "b" - }, - }; - } - - [Test] - public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_collection() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using NUnit.Framework; +using TechTalk.SpecFlow.Assist; +using TechTalk.SpecFlow.RuntimeTests.AssistTests.TestInfrastructure; + +namespace TechTalk.SpecFlow.RuntimeTests.AssistTests +{ + [TestFixture] + public class ProjectionTests + { + private SetComparisonTestObject testInstance; + private IEnumerable testCollection; + private Guid testGuid1 = Guid.NewGuid(); + private Guid testGuid2 = Guid.NewGuid(); + + [SetUp] + public void SetUp() + { + Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); + + testInstance = new SetComparisonTestObject + { + DateTimeProperty = DateTime.Today, + GuidProperty = testGuid1, + IntProperty = 1, + StringProperty = "a" + }; + testCollection = new[] + { + testInstance, + new SetComparisonTestObject + { + DateTimeProperty = DateTime.Today, + GuidProperty = testGuid2, + IntProperty = 2, + StringProperty = "b" + }, + }; + } + + [Test] + public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_collection() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); var setProjection = testCollection.ToProjection(); var tableProjection = table.ToProjection(); - Assert.IsTrue(tableProjection.SequenceEqual(setProjection)); - } - - [Test] - public void Table_with_all_columns_same_rows_but_different_order_should_not_be_sequence_equal_to_collection() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); + Assert.IsTrue(tableProjection.SequenceEqual(setProjection)); + } + + [Test] + public void Table_with_all_columns_same_rows_but_different_order_should_not_be_sequence_equal_to_collection() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); var tableProjection = table.ToProjection(); var setProjection = testCollection.ToProjection(); - Assert.IsFalse(tableProjection.SequenceEqual(setProjection)); - } - - [Test] - public void Intersection_of_matching_table_and_collection_should_have_the_size_of_the_table() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); + Assert.IsFalse(tableProjection.SequenceEqual(setProjection)); + } + + [Test] + public void Table_with_all_columns_same_rows_but_different_order_should_throw_comparison_exception_for_sequence_equal() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); + table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); + + Assert.Throws(() => + table.CompareToProjectionOfSet(testCollection, + (x, y) => x.SequenceEqual(y))); + } + + [Test] + public void Intersection_of_matching_table_and_collection_should_have_the_size_of_the_table() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); var tableProjection = table.ToProjection(); var setProjection = testCollection.ToProjection(); - Assert.AreEqual(table.RowCount, tableProjection.Intersect(setProjection).Count()); - } - - [Test] - public void Table_with_extra_columns_should_not_match_collection() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.AddDays(100).ToString(), Guid.NewGuid().ToString(), 1.ToString(), "a"); - table.AddRow(DateTime.Today.AddDays(200).ToString(), Guid.NewGuid().ToString(), 2.ToString(), "b"); - - var query = from x in testCollection + Assert.AreEqual(table.RowCount, tableProjection.Intersect(setProjection).Count()); + } + + [Test] + public void Table_with_extra_columns_should_not_match_collection() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.AddDays(100).ToString(), Guid.NewGuid().ToString(), 1.ToString(), "a"); + table.AddRow(DateTime.Today.AddDays(200).ToString(), Guid.NewGuid().ToString(), 2.ToString(), "b"); + + var query = from x in testCollection select new { x.IntProperty, x.StringProperty }; var tableProjection = table.ToProjectionOfSet(query); var queryProjection = query.ToProjection(); - Assert.AreEqual(table.RowCount, tableProjection.Except(queryProjection).Count()); - } - - [Test] - public void Table_with_subset_of_columns_with_matching_values_should_match_collection() - { - var table = CreateTableWithSubsetOfColumns(); - table.AddRow(1.ToString(), "a"); - table.AddRow(2.ToString(), "b"); - - var query = from x in testCollection + Assert.AreEqual(table.RowCount, tableProjection.Except(queryProjection).Count()); + } + + [Test] + public void Table_with_extra_columns_should_throw_comparison_exception_on_except() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.AddDays(100).ToString(), Guid.NewGuid().ToString(), 1.ToString(), "a"); + table.AddRow(DateTime.Today.AddDays(200).ToString(), Guid.NewGuid().ToString(), 2.ToString(), "b"); + + var query = from x in testCollection + select new { x.IntProperty, x.StringProperty }; + + Assert.Throws(() => + table.CompareToProjectionOfSet(query, + (x, y) => x.Except(y), "StringProperty")); + } + + [Test] + public void Table_with_subset_of_columns_with_matching_values_should_match_collection() + { + var table = CreateTableWithSubsetOfColumns(); + table.AddRow(1.ToString(), "a"); + table.AddRow(2.ToString(), "b"); + + var query = from x in testCollection select new { x.GuidProperty, x.IntProperty, x.StringProperty }; var tableProjection = table.ToProjectionOfSet(query); var queryProjection = query.ToProjection(); - Assert.AreEqual(0, tableProjection.Except(queryProjection).Count()); - } - - [Test] - public void Table_with_subset_of_columns_with_matching_values_and_order_should_be_sequence_equal_to_collection() - { - var table = CreateTableWithSubsetOfColumns(); - table.AddRow(1.ToString(), "a"); - table.AddRow(2.ToString(), "b"); - - var query = from x in testCollection + Assert.AreEqual(0, tableProjection.Except(queryProjection).Count()); + } + + [Test] + public void Table_with_subset_of_columns_with_matching_values_and_order_should_be_sequence_equal_to_collection() + { + var table = CreateTableWithSubsetOfColumns(); + table.AddRow(1.ToString(), "a"); + table.AddRow(2.ToString(), "b"); + + var query = from x in testCollection select new { x.GuidProperty, x.IntProperty, x.StringProperty }; var tableProjection = table.ToProjectionOfSet(query); var queryProjection = query.ToProjection(); - Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); - } - - [Test] - public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_queryable_collection() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); - table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); - + Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); + } + + [Test] + public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_queryable_collection() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); + table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); + var query = testCollection.AsQueryable(); var tableProjection = table.ToProjectionOfSet(query); var queryProjection = query.ToProjection(); - Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); - } - - [Test] - public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_list() - { - var table = CreateTableWithAllColumns(); - table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); - table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); - + Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); + } + + [Test] + public void Table_with_all_columns_same_rows_and_order_should_be_sequence_equal_to_list() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); + table.AddRow(DateTime.Today.ToString(), testGuid2.ToString(), 2.ToString(), "b"); + var query = testCollection.ToList(); var tableProjection = table.ToProjectionOfSet(query); var queryProjection = query.ToProjection(); - Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); - } - - [Test] - public void Table_with_single_element_and_all_columns_should_be_equal_to_matching_instance() - { - var table = CreateTableWithAllColumns(); + Assert.IsTrue(tableProjection.SequenceEqual(queryProjection)); + } + + [Test] + public void Table_with_single_element_and_all_columns_should_be_equal_to_matching_instance() + { + var table = CreateTableWithAllColumns(); table.AddRow(DateTime.Today.ToString(), testGuid1.ToString(), 1.ToString(), "a"); var tableProjection = table.ToProjection(); - Assert.AreEqual(tableProjection, testInstance); - } - - [Test] - public void Table_with_single_element_and_all_columns_should_not_be_equal_to_unmatching_instance() - { - var table = CreateTableWithAllColumns(); + Assert.AreEqual(tableProjection, testInstance); + } + + [Test] + public void Table_with_single_element_and_all_columns_should_not_be_equal_to_unmatching_instance() + { + var table = CreateTableWithAllColumns(); table.AddRow(DateTime.Today.ToString(), Guid.NewGuid().ToString(), 1.ToString(), "b"); var tableProjection = table.ToProjection(); - Assert.AreNotEqual(tableProjection, testInstance); - } - - [Test] - public void Table_with_subset_of_columns_should_be_equal_to_matching_instance() - { - var table = CreateTableWithSubsetOfColumns(); - table.AddRow(1.ToString(), "a"); - + Assert.AreNotEqual(tableProjection, testInstance); + } + + [Test] + public void Table_with_single_element_and_all_columns_should_throw_comparison_exception_for_unmatching_instance() + { + var table = CreateTableWithAllColumns(); + table.AddRow(DateTime.Today.ToString(), Guid.NewGuid().ToString(), 1.ToString(), "b"); + + Assert.Throws(() => table.CompareToProjectionOfInstance(testInstance)); + } + + [Test] + public void Table_with_subset_of_columns_should_be_equal_to_matching_instance() + { + var table = CreateTableWithSubsetOfColumns(); + table.AddRow(1.ToString(), "a"); + var instance = new { IntProperty = testInstance.IntProperty, StringProperty = testInstance.StringProperty }; var tableProjection = table.ToProjectionOfInstance(instance); - Assert.AreEqual(tableProjection, instance); - } - - [Test] - public void Table_with_subset_of_columns_should_not_be_equal_to_unmatching_instance() - { - var table = CreateTableWithSubsetOfColumns(); - table.AddRow(1.ToString(), "b"); - + Assert.AreEqual(tableProjection, instance); + } + + [Test] + public void Table_with_subset_of_columns_should_not_be_equal_to_unmatching_instance() + { + var table = CreateTableWithSubsetOfColumns(); + table.AddRow(1.ToString(), "b"); + var instance = new { IntProperty = testInstance.IntProperty, StringProperty = testInstance.StringProperty }; var tableProjection = table.ToProjectionOfInstance(instance); - Assert.AreNotEqual(tableProjection, instance); - } - - private Table CreateTableWithAllColumns() - { - return new Table("DateTimeProperty", "GuidProperty", "IntProperty", "StringProperty"); - } - - private Table CreateTableWithSubsetOfColumns() - { - return new Table("IntProperty", "StringProperty"); - } - } -} + Assert.AreNotEqual(tableProjection, instance); + } + + [Test] + public void Table_with_subset_of_columns_should_throw_comparison_exception_for_unmatching_instance() + { + var table = CreateTableWithSubsetOfColumns(); + table.AddRow(1.ToString(), "b"); + + var instance = new { IntProperty = testInstance.IntProperty, StringProperty = testInstance.StringProperty }; + + Assert.Throws(() => table.CompareToProjectionOfInstance(instance)); + } + + private Table CreateTableWithAllColumns() + { + return new Table("DateTimeProperty", "GuidProperty", "IntProperty", "StringProperty"); + } + + private Table CreateTableWithSubsetOfColumns() + { + return new Table("IntProperty", "StringProperty"); + } + } +} diff --git a/Tests/RuntimeTests/AssistTests/PropertyExtensionMethodsTests.cs b/Tests/RuntimeTests/AssistTests/PropertyExtensionMethodsTests.cs index 87976115f..f81c0dfe1 100644 --- a/Tests/RuntimeTests/AssistTests/PropertyExtensionMethodsTests.cs +++ b/Tests/RuntimeTests/AssistTests/PropertyExtensionMethodsTests.cs @@ -14,7 +14,7 @@ public void Can_get_the_property_of_an_object_through_GetPropertyValue() const string expectedValue = "John Galt"; var person = new Person { FullName = expectedValue }; - var value = person.GetPropertyValue("FullName"); + var value = person.GetMemberValue("FullName"); Assert.AreEqual(expectedValue, value); } @@ -24,7 +24,7 @@ public void Can_get_the_property_of_an_object_even_if_the_name_has_extra_spaces( { var person = new Person {FullName = "Howard Roark"}; - person.GetPropertyValue("Full Name") + person.GetMemberValue("Full Name") .ShouldEqual("Howard Roark"); } @@ -33,7 +33,7 @@ public void Can_get_the_property_of_an_object_even_if_the_casing_is_wrong() { var person = new Person { FullName = "Howard Roark" }; - person.GetPropertyValue("fullname") + person.GetMemberValue("fullname") .ShouldEqual("Howard Roark"); } @@ -43,7 +43,7 @@ public void Can_set_the_value_on_the_property_through_SetPropertyValue() const string expectedValue = "John Galt"; var person = new Person { FullName = "Howard Roark" }; - person.SetPropertyValue("FullName", expectedValue); + person.SetMemberValue("FullName", expectedValue); Assert.AreEqual(expectedValue, person.FullName); } @@ -52,7 +52,7 @@ public void Can_set_the_value_on_the_property_through_SetPropertyValue() public void Can_set_the_value_on_the_property_regardless_of_spaces() { var person = new Person { FullName = "Howard Roark" }; - person.SetPropertyValue("Full Name", "John Galt"); + person.SetMemberValue("Full Name", "John Galt"); person.FullName.ShouldEqual("John Galt"); } @@ -61,14 +61,34 @@ public void Can_set_the_value_on_the_property_regardless_of_spaces() public void Can_set_the_value_on_the_property_regardless_of_casing() { var person = new Person { FullName = "Howard Roark" }; - person.SetPropertyValue("full name", "John Galt"); + person.SetMemberValue("full name", "John Galt"); person.FullName.ShouldEqual("John Galt"); } + [Test] + public void Can_set_the_value_on_the_property_regardless_of_hyphen() + { + var person = new Person { NoBreakSupplier = "Howard Roark" }; + person.SetMemberValue("No-Break Supplier", "John Galt"); + + person.NoBreakSupplier.ShouldEqual("John Galt"); + } + + [Test] + public void Can_set_the_value_on_the_property_regardless_of_question_mark() + { + var person = new Person { ClientProfile = true }; + person.SetMemberValue("Client Profile?", true); + + person.ClientProfile.ShouldEqual(true); + } + public class Person { - public string FullName { get; set; } + public string FullName { get; set; } + public string NoBreakSupplier { get; set; } + public bool ClientProfile { get; set; } } } } diff --git a/Tests/RuntimeTests/AssistTests/SetComparisonExtensionMethods_MessageTests.cs b/Tests/RuntimeTests/AssistTests/SetComparisonExtensionMethods_MessageTests.cs index 4175e4b2e..37caba02e 100644 --- a/Tests/RuntimeTests/AssistTests/SetComparisonExtensionMethods_MessageTests.cs +++ b/Tests/RuntimeTests/AssistTests/SetComparisonExtensionMethods_MessageTests.cs @@ -10,6 +10,39 @@ public class SetComparisonExtensionMethods_MessageTests { [Test] public void Returns_the_names_of_any_fields_that_do_not_exist() + { + var table = new Table("StringField", "AFieldThatDoesNotExist", "AnotherFieldThatDoesNotExist"); + + var items = new[] { new SetComparisonTestObjectWithFields(), }; + + var exception = GetTheExceptionThrowByComparingThese(table, items); + + exception.Message.AgnosticLineBreak().ShouldEqual( + @"The following fields do not exist: +AFieldThatDoesNotExist +AnotherFieldThatDoesNotExist".AgnosticLineBreak()); + } + + [Test] + public void Returns_descriptive_message_when_two_results_exist_for_the_field_but_there_should_be_no_results() + { + var table = new Table("IntField"); + + var items = new[] { new SetComparisonTestObjectWithFields(), new SetComparisonTestObjectWithFields(), }; + + var exception = GetTheExceptionThrowByComparingThese(table, items); + + exception.Message.AgnosticLineBreak().ShouldEqual( + @" + | IntField | ++ | 0 | ++ | 0 | +".AgnosticLineBreak()); + } + + + [Test] + public void Returns_the_names_of_any_properties_that_do_not_exist() { var table = new Table("StringProperty", "AFieldThatDoesNotExist", "AnotherFieldThatDoesNotExist"); @@ -182,5 +215,18 @@ private static ComparisonException GetTheExceptionThrowByComparingThese(Table ta } return null; } + + private static ComparisonException GetTheExceptionThrowByComparingThese(Table table, SetComparisonTestObjectWithFields[] items) + { + try + { + table.CompareToSet(items); + } + catch (ComparisonException ex) + { + return ex; + } + return null; + } } } \ No newline at end of file diff --git a/Tests/RuntimeTests/AssistTests/TableHelperExtensionMethods/CreateSetHelperMethodTests_WithFunc.cs b/Tests/RuntimeTests/AssistTests/TableHelperExtensionMethods/CreateSetHelperMethodTests_WithFunc.cs index 907487d91..17bef9c7e 100644 --- a/Tests/RuntimeTests/AssistTests/TableHelperExtensionMethods/CreateSetHelperMethodTests_WithFunc.cs +++ b/Tests/RuntimeTests/AssistTests/TableHelperExtensionMethods/CreateSetHelperMethodTests_WithFunc.cs @@ -71,5 +71,23 @@ public void Still_loads_the_instance_with_the_values_from_the_table() ObjectAssertExtensions.ShouldEqual(people.First().FirstName, "John"); ObjectAssertExtensions.ShouldEqual(people.Last().FirstName, "Howard"); } + + [Test] + public void Calls_instance_creation_method_using_row_as_parameter() + { + var table = new Table("FullName", "Sex", "IsRational"); + table.AddRow("John Smith", "Male", "False"); + table.AddRow("Howard Jones", "Male", "True"); + + var people = table.CreateSet(row => new Person { FirstName = row.GetString("FullName").Split(' ').First() }); + + var john = people.First(); + var howard = people.Last(); + + ObjectAssertExtensions.ShouldEqual(john.FirstName, "John"); + ObjectAssertExtensions.ShouldEqual(john.IsRational, false); + ObjectAssertExtensions.ShouldEqual(howard.FirstName, "Howard"); + ObjectAssertExtensions.ShouldEqual(howard.IsRational, true); + } } } \ No newline at end of file diff --git a/Tests/RuntimeTests/AssistTests/TestInfrastructure/InstanceComparisonTestObjectWithFields.cs b/Tests/RuntimeTests/AssistTests/TestInfrastructure/InstanceComparisonTestObjectWithFields.cs new file mode 100644 index 000000000..1abf33b35 --- /dev/null +++ b/Tests/RuntimeTests/AssistTests/TestInfrastructure/InstanceComparisonTestObjectWithFields.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TechTalk.SpecFlow.RuntimeTests.AssistTests.TestInfrastructure +{ + public class InstanceComparisonTestObjectWithFields + { + public string StringField; + public int IntField; + } +} diff --git a/Tests/RuntimeTests/AssistTests/TestInfrastructure/SetComparisonTestObject.cs b/Tests/RuntimeTests/AssistTests/TestInfrastructure/SetComparisonTestObject.cs index 656ae9d8c..2fa528e76 100644 --- a/Tests/RuntimeTests/AssistTests/TestInfrastructure/SetComparisonTestObject.cs +++ b/Tests/RuntimeTests/AssistTests/TestInfrastructure/SetComparisonTestObject.cs @@ -12,4 +12,11 @@ class SetComparisonTestObject public Guid GuidProperty { get; set; } } + + class SetComparisonTestObjectWithFields + { + public string StringField; + public int IntField; + + } } \ No newline at end of file diff --git a/Tests/RuntimeTests/AssistTests/ValueRetrieverTests/EnumValueRetrieverTests.cs b/Tests/RuntimeTests/AssistTests/ValueRetrieverTests/EnumValueRetrieverTests.cs index 31f42c48e..6a49ad3e3 100644 --- a/Tests/RuntimeTests/AssistTests/ValueRetrieverTests/EnumValueRetrieverTests.cs +++ b/Tests/RuntimeTests/AssistTests/ValueRetrieverTests/EnumValueRetrieverTests.cs @@ -92,7 +92,7 @@ public void Throws_an_exception_when_the_value_is_empty() } [Test] - public void Does_not_throw_an_exception_when_the_value_is_null_and_enum_type_is_not_nullable() + public void Does_not_throw_an_exception_when_the_value_is_null_and_enum_type_is_nullable() { var retriever = new EnumValueRetriever(); @@ -101,30 +101,35 @@ public void Does_not_throw_an_exception_when_the_value_is_null_and_enum_type_is_ { retriever.GetValue(null, typeof (Sex?)); } - catch (InvalidOperationException exception) + catch (InvalidOperationException) { - if (exception.Message == "No enum with value {null} found") - exceptionThrown = true; + exceptionThrown = true; } exceptionThrown.ShouldBeFalse(); } [Test] - public void Does_not_throw_an_exception_when_the_value_is_empty_and_enum_type_is_not_nullable() + public void Does_not_throw_an_exception_when_the_value_is_empty_and_enum_type_is_nullable() { var retriever = new EnumValueRetriever(); var exceptionThrown = false; try { - retriever.GetValue(string.Empty, typeof (Sex)); + retriever.GetValue(string.Empty, typeof (Sex?)); } - catch (InvalidOperationException exception) + catch (InvalidOperationException) { - if (exception.Message == "No enum with value {empty} found") - exceptionThrown = true; + exceptionThrown = true; } - exceptionThrown.ShouldBeTrue(); + exceptionThrown.ShouldBeFalse(); + } + + [Test] + public void Should_return_null_when_the_value_is_empty_and_enum_type_is_nullable() + { + var retriever = new EnumValueRetriever(); + retriever.GetValue(string.Empty, typeof(Sex?)).ShouldBeNull(); } [Test] diff --git a/Tests/RuntimeTests/RuntimeTests.csproj b/Tests/RuntimeTests/RuntimeTests.csproj index 5a6e23f06..7630d1ab2 100644 --- a/Tests/RuntimeTests/RuntimeTests.csproj +++ b/Tests/RuntimeTests/RuntimeTests.csproj @@ -80,6 +80,7 @@ + @@ -168,25 +169,14 @@ + - - {453D8014-B6CD-4E86-80A8-D59F59092334} - TechTalk.SpecFlow.Generator - - - {7CCEF6D6-FC17-422E-9BED-EDD752B6496F} - TechTalk.SpecFlow.Parser - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4} TechTalk.SpecFlow - - {C0AF4A43-0C3B-47C7-86DE-79FB632B1453} - TechTalk.SpecFlow.Utils - diff --git a/Tests/RuntimeTests/StepArgumentTypeConverterTest.cs b/Tests/RuntimeTests/StepArgumentTypeConverterTest.cs index 8d1c6b75e..3aa587bd6 100644 --- a/Tests/RuntimeTests/StepArgumentTypeConverterTest.cs +++ b/Tests/RuntimeTests/StepArgumentTypeConverterTest.cs @@ -67,6 +67,13 @@ public void ShouldConvertStringToEnumerationType() Assert.That(result, Is.EqualTo(TestEnumeration.Value1)); } + [Test] + public void ShouldConvertStringToEnumerationTypeWithSpaces() + { + var result = _stepArgumentTypeConverter.Convert("Value 1", typeof(TestEnumeration), _enUSCulture); + Assert.That(result, Is.EqualTo(TestEnumeration.Value1)); + } + [Test] public void ShouldConvertStringToEnumerationTypeWithDifferingCase() { diff --git a/Tests/RuntimeTests/StepExecutionTests.cs b/Tests/RuntimeTests/StepExecutionTests.cs index 64fd9ccaf..c0c35f826 100644 --- a/Tests/RuntimeTests/StepExecutionTests.cs +++ b/Tests/RuntimeTests/StepExecutionTests.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using Rhino.Mocks; using TechTalk.SpecFlow.Infrastructure; @@ -75,6 +77,13 @@ public virtual void DistinguishByTableParam2(Table table) { } + + + [Given("Returns a Task")] + public virtual Task ReturnsATask() + { + throw new NotSupportedException("should be mocked"); + } } [Binding] @@ -275,6 +284,54 @@ public void SholdRaiseBindingErrorIfWrongParamNumber() MockRepository.VerifyAll(); } + [Test] + public void SholdCallBindingThatReturnsTask() + { + StepExecutionTestsBindings bindingInstance; + TestRunner testRunner = GetTestRunnerFor(out bindingInstance); + + bool taskFinished = false; + + bindingInstance.Expect(b => b.ReturnsATask()).Return(Task.Factory.StartNew(() => + { + Thread.Sleep(800); + taskFinished = true; + })); + + MockRepository.ReplayAll(); + + testRunner.Given("Returns a Task"); + Assert.IsTrue(taskFinished); + Assert.AreEqual(TestStatus.OK, GetLastTestStatus()); + MockRepository.VerifyAll(); + } + + [Test] + public void SholdCallBindingThatReturnsTaskAndReportError() + { + StepExecutionTestsBindings bindingInstance; + TestRunner testRunner = GetTestRunnerFor(out bindingInstance); + + bool taskFinished = false; + + bindingInstance.Expect(b => b.ReturnsATask()).Return(Task.Factory.StartNew(() => + { + Thread.Sleep(800); + taskFinished = true; + throw new Exception("catch meee"); + })); + + MockRepository.ReplayAll(); + + testRunner.Given("Returns a Task"); + Assert.IsTrue(taskFinished); + Assert.AreEqual(TestStatus.TestError, GetLastTestStatus()); + Assert.AreEqual("catch meee", ContextManagerStub.ScenarioContext.TestError.Message); + + MockRepository.VerifyAll(); + } + + } } diff --git a/Tests/RuntimeTests/StepExecutionTestsBase.cs b/Tests/RuntimeTests/StepExecutionTestsBase.cs index 68f8dd1e6..90de3c7ec 100644 --- a/Tests/RuntimeTests/StepExecutionTestsBase.cs +++ b/Tests/RuntimeTests/StepExecutionTestsBase.cs @@ -10,7 +10,6 @@ using TechTalk.SpecFlow.Bindings.Reflection; using TechTalk.SpecFlow.Infrastructure; using TechTalk.SpecFlow.Tracing; -using TechTalk.SpecFlow.Utils; using MockRepository = Rhino.Mocks.MockRepository; using TestStatus = TechTalk.SpecFlow.Infrastructure.TestStatus; diff --git a/Tests/RuntimeTests/StepTransformationTests.cs b/Tests/RuntimeTests/StepTransformationTests.cs index aa08878f0..5dfa18065 100644 --- a/Tests/RuntimeTests/StepTransformationTests.cs +++ b/Tests/RuntimeTests/StepTransformationTests.cs @@ -1,131 +1,168 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using Moq; -using NUnit.Framework; -using TechTalk.SpecFlow.Bindings; -using TechTalk.SpecFlow.Bindings.Reflection; -using TechTalk.SpecFlow.Configuration; -using TechTalk.SpecFlow.ErrorHandling; -using TechTalk.SpecFlow.Infrastructure; -using TechTalk.SpecFlow.Tracing; - -namespace TechTalk.SpecFlow.RuntimeTests -{ - public class User - { - public string Name { get; set; } - } - - [Binding] - public class UserCreator - { - [StepArgumentTransformation("user (w+)")] - public User Create(string name) - { - return new User {Name = name}; - } - - [StepArgumentTransformation] - public IEnumerable CreateUsers(Table table) - { - return table.Rows.Select(tableRow => - new User { Name = tableRow["Name"] }); - } - } - - [TestFixture] - public class StepTransformationTests - { - private readonly Mock bindingRegistryStub = new Mock(); - private readonly Mock contextManagerStub = new Mock(); - private readonly Mock methodBindingInvokerStub = new Mock(); - private readonly List stepTransformations = new List(); - - [SetUp] - public void SetUp() - { - // ScenarioContext is needed, because the [Binding]-instances live there - var scenarioContext = new ScenarioContext(null, null, null); - contextManagerStub.Setup(cm => cm.ScenarioContext).Returns(scenarioContext); - - bindingRegistryStub.Setup(br => br.GetStepTransformations()).Returns(stepTransformations); - } - - private IStepArgumentTransformationBinding CreateStepTransformationBinding(string regexString, IBindingMethod transformMethod) - { - return new StepArgumentTransformationBinding(regexString, transformMethod); - } - - private IStepArgumentTransformationBinding CreateStepTransformationBinding(string regexString, MethodInfo transformMethod) - { - return new StepArgumentTransformationBinding(regexString, new RuntimeBindingMethod(transformMethod)); - } - - [Test] - public void UserConverterShouldConvertStringToUser() - { - UserCreator stepTransformationInstance = new UserCreator(); - var transformMethod = stepTransformationInstance.GetType().GetMethod("Create"); - var stepTransformationBinding = CreateStepTransformationBinding(@"user (\w+)", transformMethod); - - Assert.True(stepTransformationBinding.Regex.IsMatch("user xyz")); - - var invoker = new BindingInvoker(new RuntimeConfiguration(), new Mock().Object); - TimeSpan duration; - var result = invoker.InvokeBinding(stepTransformationBinding, contextManagerStub.Object, new object[] { "xyz" }, new Mock().Object, out duration); - Assert.NotNull(result); - Assert.That(result.GetType(), Is.EqualTo(typeof(User))); - Assert.That(((User)result).Name, Is.EqualTo("xyz")); - } - - [Test] - public void StepArgumentTypeConverterShouldUseUserConverterForConversion() - { - UserCreator stepTransformationInstance = new UserCreator(); - var transformMethod = new RuntimeBindingMethod(stepTransformationInstance.GetType().GetMethod("Create")); - var stepTransformationBinding = CreateStepTransformationBinding(@"user (\w+)", transformMethod); - stepTransformations.Add(stepTransformationBinding); - TimeSpan duration; - var resultUser = new User(); - methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), It.IsAny(), It.IsAny(), out duration)) - .Returns(resultUser); - - var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(); - - var result = stepArgumentTypeConverter.Convert("user xyz", typeof(User), new CultureInfo("en-US")); - Assert.That(result, Is.EqualTo(resultUser)); - } - - private StepArgumentTypeConverter CreateStepArgumentTypeConverter() - { - return new StepArgumentTypeConverter(new Mock().Object, bindingRegistryStub.Object, contextManagerStub.Object, methodBindingInvokerStub.Object); - } - - [Test] - public void ShouldUseStepArgumentTransformationToConvertTable() - { - var table = new Table("Name"); - - UserCreator stepTransformationInstance = new UserCreator(); - var transformMethod = new RuntimeBindingMethod(stepTransformationInstance.GetType().GetMethod("CreateUsers")); - var stepTransformationBinding = CreateStepTransformationBinding(@"", transformMethod); - stepTransformations.Add(stepTransformationBinding); - TimeSpan duration; - var resultUsers = new User[3]; - methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), new object[] { table }, It.IsAny(), out duration)) - .Returns(resultUsers); - - var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(); - - - var result = stepArgumentTypeConverter.Convert(table, typeof(IEnumerable), new CultureInfo("en-US")); - - Assert.That(result, Is.Not.Null); - Assert.That(result, Is.EqualTo(resultUsers)); - } - } - +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Moq; +using NUnit.Framework; +using TechTalk.SpecFlow.Bindings; +using TechTalk.SpecFlow.Bindings.Reflection; +using TechTalk.SpecFlow.Configuration; +using TechTalk.SpecFlow.ErrorHandling; +using TechTalk.SpecFlow.Infrastructure; +using TechTalk.SpecFlow.Tracing; + +namespace TechTalk.SpecFlow.RuntimeTests +{ + public class User + { + public string Name { get; set; } + } + + [Binding] + public class UserCreator + { + [StepArgumentTransformation(@"user (\w+)")] + public User Create(string name) + { + return new User {Name = name}; + } + + [StepArgumentTransformation] + public IEnumerable CreateUsers(Table table) + { + return table.Rows.Select(tableRow => + new User { Name = tableRow["Name"] }); + } + } + + [TestFixture] + public class StepTransformationTests + { + private readonly Mock bindingRegistryStub = new Mock(); + private readonly Mock contextManagerStub = new Mock(); + private readonly List stepTransformations = new List(); + + [SetUp] + public void SetUp() + { + // ScenarioContext is needed, because the [Binding]-instances live there + var scenarioContext = new ScenarioContext(null, null, null); + contextManagerStub.Setup(cm => cm.ScenarioContext).Returns(scenarioContext); + + bindingRegistryStub.Setup(br => br.GetStepTransformations()).Returns(stepTransformations); + } + + private IStepArgumentTransformationBinding CreateStepTransformationBinding(MethodInfo transformMethod) + { + var regexString = transformMethod.GetCustomAttributes(typeof(StepArgumentTransformationAttribute), false) + .OfType().Select(x => x.Regex).FirstOrDefault(); + return new StepArgumentTransformationBinding(regexString, new RuntimeBindingMethod(transformMethod)); + } + + [Test] + public void UserConverterShouldConvertStringToUser() + { + UserCreator stepTransformationInstance = new UserCreator(); + var transformMethod = stepTransformationInstance.GetType().GetMethod("Create"); + var stepTransformationBinding = CreateStepTransformationBinding(transformMethod); + + Assert.True(stepTransformationBinding.Regex.IsMatch("user xyz")); + + var invoker = new BindingInvoker(new RuntimeConfiguration(), new Mock().Object); + TimeSpan duration; + var result = invoker.InvokeBinding(stepTransformationBinding, contextManagerStub.Object, new object[] { "xyz" }, new Mock().Object, out duration); + Assert.NotNull(result); + Assert.That(result.GetType(), Is.EqualTo(typeof(User))); + Assert.That(((User)result).Name, Is.EqualTo("xyz")); + } + + [Test] + public void StepArgumentTypeConverterShouldUseUserConverterForConversion() + { + UserCreator stepTransformationInstance = new UserCreator(); + var transformMethod = stepTransformationInstance.GetType().GetMethod("Create"); + var stepTransformationBinding = CreateStepTransformationBinding(transformMethod); + stepTransformations.Clear(); + stepTransformations.Add(stepTransformationBinding); + TimeSpan duration; + var resultUser = new User(); + var methodBindingInvokerStub = new Mock(); + methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), It.IsAny(), It.IsAny(), out duration)) + .Returns(resultUser); + + var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(methodBindingInvokerStub.Object); + + var result = stepArgumentTypeConverter.Convert("user xyz", typeof(User), new CultureInfo("en-US")); + Assert.That(result, Is.EqualTo(resultUser)); + } + + [Test] + public void StepArgumentTypeConverterShouldNotUseUserConverterForStringConversionIfRegexDoesNotMatch() + { + var transformMethod = typeof(UserCreator).GetMethod("Create"); + var stepTransformationBinding = CreateStepTransformationBinding(transformMethod); + stepTransformations.Clear(); + stepTransformations.Add(stepTransformationBinding); + TimeSpan duration; + var resultUser = new User(); + var methodBindingInvokerStub = new Mock(); + methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), It.IsAny(), It.IsAny(), out duration)) + .Returns(resultUser); + + var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(methodBindingInvokerStub.Object); + + var result = stepArgumentTypeConverter.CanConvert("xyz", new RuntimeBindingType(typeof(User)), new CultureInfo("en-US")); + Assert.That(result, Is.False); + } + + [Test] + public void StepArgumentTypeConverterShouldNotUseUserConverterForNonStringConvertion() + { + var transformMethod = typeof(UserCreator).GetMethod("Create"); + var stepTransformationBinding = CreateStepTransformationBinding(transformMethod); + stepTransformations.Clear(); + stepTransformations.Add(stepTransformationBinding); + TimeSpan duration; + var resultUser = new User(); + var methodBindingInvokerStub = new Mock(); + methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), It.IsAny(), It.IsAny(), out duration)) + .Returns(resultUser); + + var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(methodBindingInvokerStub.Object); + + var table = new Table("Name"); + table.AddRow("xyz"); + var result = stepArgumentTypeConverter.Convert(table, new RuntimeBindingType(typeof(User)), new CultureInfo("en-US")); + Assert.That(result, Is.Not.EqualTo(resultUser)); + } + + private StepArgumentTypeConverter CreateStepArgumentTypeConverter(IBindingInvoker bindingInvoker) + { + return new StepArgumentTypeConverter(new Mock().Object, bindingRegistryStub.Object, contextManagerStub.Object, bindingInvoker); + } + + [Test] + public void ShouldUseStepArgumentTransformationToConvertTable() + { + var table = new Table("Name"); + + UserCreator stepTransformationInstance = new UserCreator(); + var transformMethod = stepTransformationInstance.GetType().GetMethod("CreateUsers"); + var stepTransformationBinding = CreateStepTransformationBinding(transformMethod); + stepTransformations.Clear(); + stepTransformations.Add(stepTransformationBinding); + TimeSpan duration; + var resultUsers = new User[3]; + var methodBindingInvokerStub = new Mock(); + methodBindingInvokerStub.Setup(i => i.InvokeBinding(stepTransformationBinding, It.IsAny(), new object[] { table }, It.IsAny(), out duration)) + .Returns(resultUsers); + + var stepArgumentTypeConverter = CreateStepArgumentTypeConverter(methodBindingInvokerStub.Object); + var result = stepArgumentTypeConverter.Convert(table, typeof(IEnumerable), new CultureInfo("en-US")); + + Assert.That(result, Is.Not.Null); + Assert.That(result, Is.EqualTo(resultUsers)); + } + } } \ No newline at end of file diff --git a/Tests/RuntimeTests/TableConverterTests.cs b/Tests/RuntimeTests/TableConverterTests.cs new file mode 100644 index 000000000..bb52548f8 --- /dev/null +++ b/Tests/RuntimeTests/TableConverterTests.cs @@ -0,0 +1,321 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using NUnit.Framework; +using Rhino.Mocks; +using Constraints = Rhino.Mocks.Constraints; +using Should; +using TechTalk.SpecFlow.RuntimeTests.AssistTests.ExampleEntities; +using TestStatus = TechTalk.SpecFlow.Infrastructure.TestStatus; +using Rhino.Mocks.Interfaces; + +namespace TechTalk.SpecFlow.RuntimeTests +{ + [Binding] + public class BindingsForTableConverterTests + { + [Given("sample step with Table to Person conversion")] + public virtual void PersonArg(Person param) + { + + } + + [Given("sample step with Table to Person array conversion")] + public virtual void PersonArrayArg(Person[] param) + { + + } + + [Given("sample step with Table to IEnumerable conversion")] + public virtual void UserEnumerationArg(IEnumerable param) + { + + } + + [Given("sample step with Table to UserPerson conversion")] + public virtual void UserPersonArg(UserPerson param) + { + + } + + [Given("sample step with Table to UserWithConstructorParameters conversion")] + public virtual void UserWithConstructorParametersArg(UserWithConstructorParameters param) + { + + } + + [StepArgumentTransformation(@"user (\w+)")] + public virtual User Create(string name) + { + return new User { Name = name }; + } + + [StepArgumentTransformation(@"(\w+) (\w+)")] + public virtual Person Create(string firstName, string lastName) + { + return new Person { FirstName = firstName, LastName = lastName }; + } + } + + public class UserPerson + { + public User User { get; set; } + public Person Person { get; set; } + public Guid Guid { get; set; } + public Style Style { get; set; } + } + + public class UserWithConstructorParameters + { + public int Id { get; private set; } + public string Name { get; set; } + + public UserWithConstructorParameters() + { + + } + + public UserWithConstructorParameters(int id) + { + Id = id; + } + } + + [TestFixture] + public class TableConverterTests : StepExecutionTestsBase + { + private T ConversionTest( + string given, Action stepDefinition, + Table table, bool shouldSucceed) + { + T result = default(T); + BindingsForTableConverterTests bindingInstance; + TestRunner testRunner = GetTestRunnerFor(out bindingInstance); + + bindingInstance.Expect(b => b.Create(null)).IgnoreArguments().CallOriginalMethod(OriginalCallOptions.NoExpectation); + bindingInstance.Expect(b => b.Create(null, null)).IgnoreArguments().CallOriginalMethod(OriginalCallOptions.NoExpectation); + bindingInstance.Expect(b => stepDefinition(b, default(T))) + .IgnoreArguments().Repeat.Times(shouldSucceed ? 1 : 0) + .WhenCalled(mi => result = (T)mi.Arguments[0]); + MockRepository.ReplayAll(); + + testRunner.Given(given, null, table); + + Assert.AreEqual(shouldSucceed ? TestStatus.OK : TestStatus.TestError, GetLastTestStatus()); + MockRepository.VerifyAll(); + + return result; + } + + private Person PersonConversionTest(Table table, bool shouldSucceed) + { + return ConversionTest("sample step with Table to Person conversion", (b, a) => b.PersonArg(a), table, shouldSucceed); + } + + private Person[] PersonArrayConversionTest(Table table, bool shouldSucceed) + { + return ConversionTest("sample step with Table to Person array conversion", (b, a) => b.PersonArrayArg(a), table, shouldSucceed); + } + + private IEnumerable UserEnumerationConversionTest(Table table, bool shouldSucceed) + { + return ConversionTest>("sample step with Table to IEnumerable conversion", (b, a) => b.UserEnumerationArg(a), table, shouldSucceed); + } + + private UserPerson UserPersonConversionTest(Table table, bool shouldSucceed) + { + return ConversionTest("sample step with Table to UserPerson conversion", (b, a) => b.UserPersonArg(a), table, shouldSucceed); + } + + private UserWithConstructorParameters UserWithConstructorParametersConversionTest(Table table, bool shouldSucceed) + { + return ConversionTest("sample step with Table to UserWithConstructorParameters conversion", (b, a) => b.UserWithConstructorParametersArg(a), table, shouldSucceed); + } + + [Test] + public void Table_converters_will_return_an_instance_of_T() + { + var table = new Table("Field", "Value"); + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + } + + [Test] + public void Table_converters_will_set_values_with_a_vertical_table_when_there_is_one_row_and_one_column() + { + var table = new Table("FirstName"); + table.AddRow("Homer"); + + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.FirstName.ShouldEqual("Homer"); + } + + [Test] + public void When_one_row_exists_with_two_headers_and_the_first_row_value_is_not_a_property_then_treat_as_horizontal_table() + { + var table = new Table("FirstName", "LastName"); + table.AddRow("Homer", "Simpson"); + + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.FirstName.ShouldEqual("Homer"); + result.LastName.ShouldEqual("Simpson"); + } + + [Test] + public void When_one_row_exists_with_two_headers_and_the_first_row_value_is_a_property_then_treat_as_a_vertical_table() + { + var table = new Table("FirstName", "LastName"); + table.AddRow("FirstName", "Homer"); + + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.FirstName.ShouldEqual("Homer"); + result.LastName.ShouldBeNull(); + } + + [Test] + public void When_one_row_exists_with_two_headers_and_the_first_row_value_is_a_property_without_perfect_name_match_then_treat_as_a_vertical_table() + { + var table = new Table("FirstName", "LastName"); + table.AddRow("First name", "Homer"); + + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.FirstName.ShouldEqual("Homer"); + result.LastName.ShouldBeNull(); + } + + [Test] + public void When_one_row_exists_with_three_headers_then_treat_as_horizontal_table() + { + var table = new Table("FirstName", "MiddleInitial", "LastName"); + table.AddRow("Homer", "J", "Simpson"); + + var result = PersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.FirstName.ShouldEqual("Homer"); + result.MiddleInitial.ShouldEqual('J'); + result.LastName.ShouldEqual("Simpson"); + } + + [Test] + public void Table_converters_will_return_an_empty_array_when_converting_an_empty_horizontal_table() + { + var table = new Table("FirstName", "MiddleInitial", "LastName"); + + var result = PersonArrayConversionTest(table, true); + result.ShouldNotBeNull(); + result.ShouldBeEmpty(); + } + + [Test] + public void Table_converters_will_return_an_array_with_one_item_for_each_row_of_an_horizontal_table() + { + var table = new Table("FirstName", "MiddleInitial", "LastName"); + table.AddRow("Homer", "J", "Simpson"); + table.AddRow("Mona", "J", "Simpson"); + + var result = PersonArrayConversionTest(table, true); + result.ShouldNotBeNull(); + result.Length.ShouldEqual(2); + result[0].ShouldNotBeNull(); + result[0].FirstName.ShouldEqual("Homer"); + result[1].ShouldNotBeNull(); + result[1].FirstName.ShouldEqual("Mona"); + } + + [Test] + public void Table_converters_will_successfully_convert_an_horizontal_table_to_an_IEnumerable() + { + var table = new Table("Name"); + table.AddRow("Homer"); + table.AddRow("Mona"); + + var result = UserEnumerationConversionTest(table, true); + result.ShouldNotBeNull(); + result.Count().ShouldEqual(2); + Assert.That(result.All(x => x != null), Is.True); + } + + [Test] + public void Table_converters_will_not_convert_a_vertical_table_to_an_array() + { + var table = new Table("Field", "Value"); + table.AddRow("FirstName", "Homer"); + table.AddRow("LastName", "Simpson"); + + PersonArrayConversionTest(table, false); + } + + [Test] + public void Table_converters_will_not_convert_an_horizontal_with_multiple_rows_table_to_a_single_object() + { + var table = new Table("FirstName", "MiddleInitial", "LastName"); + table.AddRow("Homer", "J", "Simpson"); + table.AddRow("Mona", "J", "Simpson"); + + PersonConversionTest(table, false); + } + + [Test] + public void Table_converters_will_use_other_converters_to_convert_each_value_from_the_table() + { + var table = new Table("User", "Person", "Guid", "Style"); + table.AddRow("user Admin", "Homer Simpson", "B48D8AF4-405F-4286-B83E-774EA773CFA3", "very cool"); + + var result = UserPersonConversionTest(table, true); + result.ShouldNotBeNull(); + result.User.ShouldNotBeNull(); + result.User.Name.ShouldEqual("Admin"); + result.Person.ShouldNotBeNull(); + result.Person.FirstName.ShouldEqual("Homer"); + result.Person.LastName.ShouldEqual("Simpson"); + result.Guid.ShouldEqual(new Guid("B48D8AF4-405F-4286-B83E-774EA773CFA3")); + result.Style.ShouldEqual(Style.VeryCool); + } + + [Test] + public void Table_converters_will_try_to_convert_the_string_value_of_a_single_header_horizontal_table_if_there_is_no_conversion_available_for_the_pivoted_vertical_tables() + { + var table = new Table("Person"); + table.AddRow("Homer Simpson"); + table.AddRow("Mona Simpson"); + + var result = PersonArrayConversionTest(table, true); + result.ShouldNotBeNull(); + result.Length.ShouldEqual(2); + result[0].ShouldNotBeNull(); + result[0].FirstName.ShouldEqual("Homer"); + result[0].LastName.ShouldEqual("Simpson"); + result[1].ShouldNotBeNull(); + result[1].FirstName.ShouldEqual("Mona"); + result[1].LastName.ShouldEqual("Simpson"); + } + + [Test] + public void Table_converters_will_use_the_constructor_that_includes_the_maximum_number_of_columns_with_no_matching_writable_property() + { + var table = new Table("Id", "Name"); + table.AddRow("444", "Homer"); + + var result = UserWithConstructorParametersConversionTest(table, true); + result.ShouldNotBeNull(); + result.Id.ShouldEqual(444); + result.Name.ShouldEqual("Homer"); + } + + [Test] + public void Table_converters_will_ignore_columns_with_not_matching_writable_property_or_constructor_parameter() + { + var table = new Table("Id", "Name", "Comment"); + table.AddRow("444", "Homer", "Some comment"); + + var result = UserWithConstructorParametersConversionTest(table, true); + result.ShouldNotBeNull(); + result.Id.ShouldEqual(444); + result.Name.ShouldEqual("Homer"); + } + } +} diff --git a/Tools/Program.cs b/Tools/Program.cs index 8025264eb..66e159a6f 100644 --- a/Tools/Program.cs +++ b/Tools/Program.cs @@ -52,7 +52,7 @@ public static void StepDefinitionReport( public static void NUnitExecutionReport([Required(Description = "Visual Studio Project File containing specs")] string projectFile, [Optional("TestResult.xml", Description = "Xml Test Result file generated by NUnit. Defaults to TestResult.xml")] string xmlTestResult, [Optional("", Description = "Xslt file to use, defaults to built-in stylesheet if not provided")] string xsltFile, - [Optional("TestResult.txt", "testOutput")] string labeledTestOutput, + [Optional("TestResult.txt", "testOutput", Description = "The labeled test output file generated by nunit-console. Defaults to TestResult.txt")] string labeledTestOutput, [Optional("TestResult.html", "out", Description = "Generated Output File. Defaults to TestResult.html")] string outputFile) { var reportParameters = diff --git a/changelog.txt b/changelog.txt index 7b98fd23b..1bccd5e3f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,12 @@ +1.9.3 - 2014/06/22 (Specflow Ms Test Report) +New Features: ++ Specflow Test Execution report now includes the "tags" in the Nunit Report which can be displayed through a custom XSLT by the users. + + 1.9.2 - 2013/03/25 (Visual Studio Integration update) Fixed issues: ++ Empty strings in tables get converted to null in case of nullable enum as target types when using CreateSet + VS2010/VS2012: Visual Studio freezes while editing keywords (Issue 128) + VS2010/VS2012: Linked app.config now works (Issue 255, by ArildF) + VS2012: Run SpecFlow Scenarios didn't work in VS2012 Update 1 (Issue 273) @@ -13,6 +19,7 @@ Fixed issues: New Features: + VS2010/VS2012: Table outlining support in the editor (Issue 244, by RaringCoder) ++ Assist: Added overload to CreateSet accepting a Func to create each instance 1.9.1 - 2012/10/12 (Visual Studio Integration update)