File: ViewComponents\DefaultViewComponentActivatorTests.cs
Web Access
Project: src\src\Mvc\Mvc.ViewFeatures\test\Microsoft.AspNetCore.Mvc.ViewFeatures.Test.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures.Test)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Moq;
 
namespace Microsoft.AspNetCore.Mvc.ViewComponents;
 
public class DefaultViewComponentActivatorTests
{
    [Fact]
    public void DefaultViewComponentActivator_ActivatesViewComponentContext()
    {
        // Arrange
        var expectedInstance = new TestViewComponent();
 
        var typeActivator = new Mock<ITypeActivatorCache>();
        typeActivator
            .Setup(ta => ta.CreateInstance<object>(It.IsAny<IServiceProvider>(), It.IsAny<Type>()))
            .Returns(expectedInstance);
 
        var activator = new DefaultViewComponentActivator(typeActivator.Object);
 
        var context = CreateContext(typeof(TestViewComponent));
        expectedInstance.ViewComponentContext = context;
 
        // Act
        var instance = activator.Create(context) as ViewComponent;
 
        // Assert
        Assert.NotNull(instance);
        Assert.Same(context, instance.ViewComponentContext);
    }
 
    [Fact]
    public void DefaultViewComponentActivator_ActivatesViewComponentContext_IgnoresNonPublic()
    {
        // Arrange
        var expectedInstance = new VisibilityViewComponent();
 
        var typeActivator = new Mock<ITypeActivatorCache>();
        typeActivator
            .Setup(ta => ta.CreateInstance<object>(It.IsAny<IServiceProvider>(), It.IsAny<Type>()))
            .Returns(expectedInstance);
 
        var activator = new DefaultViewComponentActivator(typeActivator.Object);
 
        var context = CreateContext(typeof(VisibilityViewComponent));
        expectedInstance.ViewComponentContext = context;
 
        // Act
        var instance = activator.Create(context) as VisibilityViewComponent;
 
        // Assert
        Assert.NotNull(instance);
        Assert.Same(context, instance.ViewComponentContext);
        Assert.Null(instance.C);
    }
 
    [Fact]
    public async Task DefaultViewComponentActivator_ReleaseAsync_PrefersAsyncDisposableOverDisposable()
    {
        // Arrange
        var instance = new SyncAndAsyncDisposableViewComponent();
 
        var activator = new DefaultViewComponentActivator(Mock.Of<ITypeActivatorCache>());
 
        var context = CreateContext(typeof(SyncAndAsyncDisposableViewComponent));
 
        // Act
        await activator.ReleaseAsync(context, instance);
 
        // Assert
        Assert.True(instance.AsyncDisposed);
        Assert.False(instance.SyncDisposed);
    }
 
    private static ViewComponentContext CreateContext(Type componentType)
    {
        return new ViewComponentContext
        {
            ViewComponentDescriptor = new ViewComponentDescriptor
            {
                TypeInfo = componentType.GetTypeInfo()
            },
            ViewContext = new ViewContext
            {
                HttpContext = new DefaultHttpContext
                {
                    RequestServices = Mock.Of<IServiceProvider>()
                }
            }
        };
    }
 
    private class TestViewComponent : ViewComponent
    {
        public Task ExecuteAsync()
        {
            throw new NotImplementedException();
        }
    }
 
    private class VisibilityViewComponent : ViewComponent
    {
        [ViewComponentContext]
        protected internal ViewComponentContext C { get; set; }
    }
 
    public class ActivablePropertiesViewComponent : IDisposable
    {
        [ViewComponentContext]
        public ViewComponentContext Context { get; set; }
 
        public bool Disposed { get; private set; }
 
        public void Dispose()
        {
            Disposed = true;
        }
 
        public string Invoke()
        {
            return "something";
        }
    }
 
    public class AsyncDisposableViewComponent : IAsyncDisposable
    {
        [ViewComponentContext]
        public ViewComponentContext Context { get; set; }
 
        public bool Disposed { get; private set; }
 
        public ValueTask DisposeAsync()
        {
            Disposed = true;
            return default;
        }
 
        public string Invoke()
        {
            return "something";
        }
    }
 
    public class SyncAndAsyncDisposableViewComponent : IDisposable, IAsyncDisposable
    {
        [ViewComponentContext]
        public ViewComponentContext Context { get; set; }
 
        public bool AsyncDisposed { get; private set; }
        public bool SyncDisposed { get; private set; }
 
        public void Dispose() => SyncDisposed = true;
 
        public ValueTask DisposeAsync()
        {
            AsyncDisposed = true;
            return default;
        }
 
        public string Invoke()
        {
            return "something";
        }
    }
}