Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to convert type to same type or one of base types with StepArgumentTransformation #1110

Merged
18 changes: 10 additions & 8 deletions TechTalk.SpecFlow/Bindings/StepArgumentTypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ protected virtual IStepArgumentTransformationBinding GetMatchingStepTransformati
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);

var convertToType = typeToConvertTo as RuntimeBindingType;
if (convertToType != null && convertToType.Type.IsAssignableFrom(value.GetType()))
return value;

return ConvertSimple(typeToConvertTo, value, cultureInfo);
}

Expand Down Expand Up @@ -79,15 +80,16 @@ private object[] GetStepTransformationArgumentsFromRegex(IStepArgumentTransforma

public bool CanConvert(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo)
{
if (value == null) throw new ArgumentNullException("value");

if (typeToConvertTo == value.GetType())
return true;
if (value == null) throw new ArgumentNullException("value");

var stepTransformation = GetMatchingStepTransformation(value, typeToConvertTo, false);
if (stepTransformation != null)
return true;

var convertToType = typeToConvertTo as RuntimeBindingType;
if (convertToType != null && convertToType.Type.IsAssignableFrom(value.GetType()))
return true;

return CanConvertSimple(typeToConvertTo, value, cultureInfo);
}

Expand Down
5 changes: 1 addition & 4 deletions TechTalk.SpecFlow/Infrastructure/TestExecutionEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,10 +436,7 @@ private object ConvertArg(object value, IBindingType typeToConvertTo)
{
Debug.Assert(value != null);
Debug.Assert(typeToConvertTo != null);

if (typeToConvertTo.IsAssignableTo(value.GetType()))
return value;


return stepArgumentTypeConverter.Convert(value, typeToConvertTo, FeatureContext.BindingCulture);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static object Convert(this IStepArgumentTypeConverter converter, object v
Arg<string>.Is.Equal(argument),
Arg<IBindingType>.Matches(bt => bt.TypeEquals(type)),
Arg<CultureInfo>.Is.Anything);
}
}
}

[Binding]
Expand Down Expand Up @@ -148,6 +148,7 @@ public void ShouldCallTheOnlyThatCanConvertWithTable()
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetCanConvertMethodFilter("argument", typeof(double))).Return(true);
StepArgumentTypeConverterStub.Stub(c => c.CanConvert(null, null, null)).IgnoreArguments().Return(false);

StepArgumentTypeConverterStub.Expect(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(table, typeof(Table))).Return(table);
StepArgumentTypeConverterStub.Expect(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter("argument", typeof(double))).Return(1.23);
bindingInstance.Expect(b => b.DoubleArgWithTable(1.23, table));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,15 @@ public void ShouldCallTheUserConverterToConvertTableWithTableAndMultilineArg()

Table table = new Table("h1");
var user = new User();

var multiLineArg = "multi-line arg";
// return false unless its a User
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetCanConvertMethodFilter(table, typeof(User))).Return(true);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetCanConvertMethodFilter(table, typeof(User))).Return(true);
StepArgumentTypeConverterStub.Stub(c => c.CanConvert(null, null, null)).IgnoreArguments().Return(false);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(multiLineArg, typeof(string))).Return(multiLineArg);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(table, typeof(User))).Return(user);


var multiLineArg = "multi-line arg";

bindingInstance.Expect(b => b.MultilineArgumentAndTable(multiLineArg, user));
MockRepository.ReplayAll();

Expand All @@ -90,14 +92,16 @@ public void ShouldCallTheUserConverterToConvertTableWithTableAndMultilineArgAndP
Table table = new Table("h1");
string argumentValue = "argument";
var user = new User();

var multiLineArg = "multi-line arg";
// return false unless its a User
// must also stub CanConvert & Convert for the string argument as we've introduced a parameter
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetCanConvertMethodFilter(table, typeof(User))).Return(true);
StepArgumentTypeConverterStub.Stub(c => c.CanConvert(null, null, null)).IgnoreArguments().Return(false);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(table, typeof(User))).Return(user);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(argumentValue, typeof(string))).Return(argumentValue);
StepArgumentTypeConverterStub.Stub(LegacyStepArgumentTypeConverterExtensions.GetConvertMethodFilter(multiLineArg, typeof(string))).Return(multiLineArg);

var multiLineArg = "multi-line arg";

bindingInstance.Expect(b => b.ParameterMultilineArgumentAndTable(argumentValue, multiLineArg, user));
MockRepository.ReplayAll();

Expand Down
73 changes: 73 additions & 0 deletions Tests/TechTalk.SpecFlow.RuntimeTests/StepTransformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ public IEnumerable<User> CreateUsers(Table table)
}
}

[Binding]
public class TypeToTypeConverter
{
[StepArgumentTransformation("string (w+)")]
public string StringToStringConvertRegex(string value)
{
return string.Concat("prefix ", value);
}

[StepArgumentTransformation]
public string StringToStringConvert(string value)
{
return string.Concat("prefix ", value);
}

[StepArgumentTransformation]
public Table TableToTableConvert(Table table)
{
var transformedTable = new List<string>();
transformedTable.Add("transformed column");
transformedTable.AddRange(table.Header);

return new Table(transformedTable.ToArray());
}
}

[TestFixture]
public class StepTransformationTests
{
Expand Down Expand Up @@ -80,6 +106,53 @@ public void UserConverterShouldConvertStringToUser()
Assert.NotNull(result);
Assert.That(result.GetType(), Is.EqualTo(typeof(User)));
Assert.That(((User)result).Name, Is.EqualTo("xyz"));
}

[Test]
public void TypeToTypeConverterShouldConvertStringToStringUsingRegex()
{
TypeToTypeConverter stepTransformationInstance = new TypeToTypeConverter();
var transformMethod = stepTransformationInstance.GetType().GetMethod("StringToStringConvertRegex");
var stepTransformationBinding = CreateStepTransformationBinding(@"string (\w+)", transformMethod);

Assert.True(stepTransformationBinding.Regex.IsMatch("string xyz"));

var invoker = new BindingInvoker(ConfigurationLoader.GetDefault(), new Mock<IErrorProvider>().Object);
TimeSpan duration;
var result = invoker.InvokeBinding(stepTransformationBinding, contextManagerStub.Object, new object[] { "xyz" }, new Mock<ITestTracer>().Object, out duration);
Assert.NotNull(result);
Assert.That(result.GetType(), Is.EqualTo(typeof(string)));
Assert.That(((string)result), Is.EqualTo("prefix xyz"));
}

[Test]
public void TypeToTypeConverterShouldConvertStringToString()
{
TypeToTypeConverter stepTransformationInstance = new TypeToTypeConverter();
var transformMethod = stepTransformationInstance.GetType().GetMethod("StringToStringConvert");
var stepTransformationBinding = CreateStepTransformationBinding(@"", transformMethod);

var invoker = new BindingInvoker(ConfigurationLoader.GetDefault(), new Mock<IErrorProvider>().Object);
TimeSpan duration;
var result = invoker.InvokeBinding(stepTransformationBinding, contextManagerStub.Object, new object[] { "xyz" }, new Mock<ITestTracer>().Object, out duration);
Assert.NotNull(result);
Assert.That(result.GetType(), Is.EqualTo(typeof(string)));
Assert.That(((string)result), Is.EqualTo("prefix xyz"));
}

[Test]
public void TypeToTypeConverterShouldConvertTableToTable()
{
TypeToTypeConverter stepTransformationInstance = new TypeToTypeConverter();
var transformMethod = stepTransformationInstance.GetType().GetMethod("TableToTableConvert");
var stepTransformationBinding = CreateStepTransformationBinding(@"", transformMethod);

var invoker = new BindingInvoker(ConfigurationLoader.GetDefault(), new Mock<IErrorProvider>().Object);
TimeSpan duration;
var result = invoker.InvokeBinding(stepTransformationBinding, contextManagerStub.Object, new object[] { new Table("h1") }, new Mock<ITestTracer>().Object, out duration);
Assert.NotNull(result);
Assert.That(result.GetType(), Is.EqualTo(typeof(Table)));
Assert.That(((Table)result).Header, Is.EqualTo(new string[] { "transformed column", "h1" }));
}

[Test]
Expand Down
5 changes: 5 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
2.4 - 2018-??-??
New Features:
+ Added ability to convert type to same type or one of the base types https://github.com/techtalk/SpecFlow/pull/1110


Fixes:
+ Expose ScenarioInfo.Description parameter to get from context https://github.com/techtalk/SpecFlow/pull/1078
+ Resolved loading plugins multiple times. Issue: https://github.com/techtalk/SpecFlow/issues/888


2.3.2 - 2018-04-17
Fixes:
+ Fix issue when using environment variables in a generator plugin's path https://github.com/techtalk/SpecFlow/pull/1045
Expand Down