Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions src/CommunityToolkit.Maui.UnitTests/Behaviors/BaseBehaviorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using CommunityToolkit.Maui.UnitTests.Mocks;
using FluentAssertions;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Behaviors;

public class BaseBehaviorTests
{
[Fact]
public void AttachAndDetachCallsShouldCorrectlyAssignView()
{
var label = new Label();
var mockBehavior = new MockBehavior();

mockBehavior.AssertViewIsNull();
mockBehavior.IsAttached.Should().BeFalse();

label.Behaviors.Add(mockBehavior);

mockBehavior.AssertViewIsEqual(label);
mockBehavior.IsAttached.Should().BeTrue();

label.Behaviors.Remove(mockBehavior);

mockBehavior.AssertViewIsNull();
mockBehavior.IsAttached.Should().BeFalse();
}

[Fact]
public void ViewPropertyChangesShouldBeHandledWithinBehavior()
{
var label = new Label();
var mockBehavior = new MockBehavior();

label.Behaviors.Add(mockBehavior);

mockBehavior.PropertyChanges.Should().BeEmpty();

label.Text = "Text";

mockBehavior.PropertyChanges.Should().ContainSingle();
var change = mockBehavior.PropertyChanges.Single();

change.Should().NotBeNull();
change.PropertyName.Should().Be(nameof(Label.Text));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.Globalization;
using CommunityToolkit.Maui.Converters;
using CommunityToolkit.Maui.UnitTests.Mocks;
using FluentAssertions;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Converters;

public abstract class BaseConverterOneWayTests
{
[Theory]
[InlineData(4.123, typeof(double))]
[InlineData(4, typeof(int))]
public abstract void Convert_WithMismatchedTargetType(object? inputValue, Type targetType);

[Theory]
[InlineData(4.123, typeof(string))]
[InlineData(true, typeof(string))]
public abstract void Convert_WithInvalidValueType(object? inputValue, Type targetType);

[Theory]
[InlineData(1, typeof(string))]
public void Convert_ShouldCorrectlyReturnValueWithMatchingTargetType(object? inputValue, Type targetType)
{
ICommunityToolkitValueConverter converter = CreateConverter();

Assert.Equal("Two", converter.Convert(inputValue, targetType, null, CultureInfo.CurrentCulture));
}

[Fact]
public void Setting_DefaultConvertBackReturnValue_WillThrowNotSupportedException()
{
ICommunityToolkitValueConverter converter = CreateConverter();

Assert.Throws<NotSupportedException>(() => new MockOneWayConverter(["One", "Two", "Three"])
{
DefaultConvertReturnValue = "Three",
DefaultConvertBackReturnValue = 1
});
}

protected static BaseConverter<int, string> CreateConverter() => new MockOneWayConverter(["One", "Two", "Three"])
{
DefaultConvertReturnValue = "Three"
};

protected BaseConverterOneWayTests(bool suppressExceptions)
{
new Options().SetShouldSuppressExceptionsInConverters(suppressExceptions);
}
}

/// <summary>
/// Unit tests that target <see cref="BaseConverter{TFrom,TTo}"/> and their public APIs with <see cref="Options.SetShouldSuppressExceptionsInConverters"/> == false.
/// </summary>
public class BaseConverterOneWayTestsWithExceptionsEnabled : BaseConverterOneWayTests
{
public BaseConverterOneWayTestsWithExceptionsEnabled() : base(false)
{
}

public override void Convert_WithMismatchedTargetType(object? inputValue, Type targetType)
{
ICommunityToolkitValueConverter converter = CreateConverter();

var exception = Assert.Throws<ArgumentException>(() => converter.Convert(inputValue, targetType, null, CultureInfo.CurrentCulture));

exception.Message.Should().Be($"targetType needs to be assignable from {converter.ToType}. (Parameter 'targetType')");
}

public override void Convert_WithInvalidValueType(object? inputValue, Type targetType)
{
ICommunityToolkitValueConverter converter = CreateConverter();

var exception = Assert.Throws<ArgumentException>(() => converter.Convert(inputValue, targetType, null, CultureInfo.CurrentCulture));

exception.Message.Should().Be($"Value needs to be of type {converter.FromType} (Parameter 'value')");
}
}

/// <summary>
/// Unit tests that target <see cref="BaseConverter{TFrom,TTo}"/> and their public APIs with <see cref="Options.SetShouldSuppressExceptionsInConverters"/> == true.
/// </summary>
public class BaseConverterOneWayTestsWithExceptionsSuppressed : BaseConverterOneWayTests
{
public BaseConverterOneWayTestsWithExceptionsSuppressed() : base(true)
{
}

public override void Convert_WithMismatchedTargetType(object? inputValue, Type targetType)
{
ICommunityToolkitValueConverter converter = CreateConverter();

converter.Convert(inputValue, targetType, null, CultureInfo.CurrentCulture).Should().Be(converter.DefaultConvertReturnValue);
}

public override void Convert_WithInvalidValueType(object? inputValue, Type targetType)
{
ICommunityToolkitValueConverter converter = CreateConverter();

converter.Convert(inputValue, targetType, null, CultureInfo.CurrentCulture).Should().Be(converter.DefaultConvertReturnValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using System.Globalization;
using CommunityToolkit.Maui.Converters;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Converters;

public abstract class BaseOneWayConverterTest<TConverter> : ConverterTest<TConverter> where TConverter : ICommunityToolkitValueConverter, new()
{
[Fact]
public void ConvertBack_ShouldThrowNotSupportedException()
{
var options = new Options();
options.SetShouldSuppressExceptionsInConverters(true);

var converter = InitializeConverterForInvalidConverterTests();

Assert.ThrowsAny<NotSupportedException>(() => converter.ConvertBack(GetInvalidConvertBackValue(), converter.FromType, null, CultureInfo.CurrentCulture));
}
}

public abstract class BaseConverterTest<TConverter> : ConverterTest<TConverter> where TConverter : ICommunityToolkitValueConverter, new()
{
[Fact]
public void InvalidConvertBackValue_ShouldThrowException()
{
var options = new Options();
options.SetShouldSuppressExceptionsInConverters(false);

var converter = InitializeConverterForInvalidConverterTests();

Assert.ThrowsAny<ArgumentException>(() => converter.ConvertBack(GetInvalidConvertBackValue(), converter.FromType, null, CultureInfo.CurrentCulture));
}

[Fact]
public void InvalidConvertBackValue_ShouldSuppressExceptionsInConverters_ShouldReturnDefaultConvertValue()
{
var options = new Options();
options.SetShouldSuppressExceptionsInConverters(true);

var converter = InitializeConverterForInvalidConverterTests();

var result = converter.ConvertBack(GetInvalidConvertBackValue(), converter.FromType, null, CultureInfo.CurrentCulture);

Assert.Equal(converter.DefaultConvertBackReturnValue, result);
}
}

public abstract class ConverterTest<TConverter> : BaseHandlerTest where TConverter : ICommunityToolkitValueConverter, new()
{
[Fact]
public void InvalidConvertValue_ShouldThrowException()
{
var options = new Options();
options.SetShouldSuppressExceptionsInConverters(false);

var converter = InitializeConverterForInvalidConverterTests();

Assert.ThrowsAny<ArgumentException>(() => converter.Convert(GetInvalidConvertFromValue(), converter.ToType, null, CultureInfo.CurrentCulture));
}

[Fact]
public void InvalidConverterValue_ShouldSuppressExceptionsInConverters_ShouldReturnDefaultConvertValue()
{
var options = new Options();
options.SetShouldSuppressExceptionsInConverters(true);

var converter = InitializeConverterForInvalidConverterTests();

var result = converter.Convert(GetInvalidConvertFromValue(), converter.ToType, null, CultureInfo.CurrentCulture);

Assert.Equal(converter.DefaultConvertReturnValue, result);
}

protected virtual object? GetInvalidConvertBackValue() => GetInvalidValue(InitializeConverterForInvalidConverterTests().ToType);
protected virtual object? GetInvalidConvertFromValue() => GetInvalidValue(InitializeConverterForInvalidConverterTests().FromType);
protected virtual TConverter InitializeConverterForInvalidConverterTests() => new();

static object GetInvalidValue(Type type)
{
if (type != typeof(string))
{
return string.Empty;
}

if (type != typeof(bool))
{
return true;
}

throw new NotImplementedException($"Invalid value not valid for {typeof(TConverter).Name}. If {nameof(InvalidConvertValue_ShouldThrowException)} is failing, please override {nameof(GetInvalidConvertFromValue)} and provide an invalid value. Otherwise, override {nameof(GetInvalidConvertBackValue)} and provide an invalid value");
}
}
Loading