/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Mvc/Mvc.ViewFeatures/test/ViewComponentResultTest.cs
747 строк
27 KB
Pranav K
Enable IDE2000 (#39803)
27 янв 2022, 19:02
Не верифицирован
27 янв 2022, 19:02
de3019b
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Reflection; using System.Text; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ViewComponents; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers; using Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Microsoft.Extensions.WebEncoders.Testing; using Microsoft.Net.Http.Headers; using Moq; namespace Microsoft.AspNetCore.Mvc; public class ViewComponentResultTest { private readonly ITempDataDictionary _tempDataDictionary = new TempDataDictionary(new DefaultHttpContext(), Mock.Of<ITempDataProvider>()); [Fact] public void Model_ExposesViewDataModel() { // Arrange var customModel = new object(); var viewResult = new ViewComponentResult { ViewData = new ViewDataDictionary(new EmptyModelMetadataProvider()) { Model = customModel }, }; // Act & Assert Assert.Same(customModel, viewResult.Model); } [Fact] public async Task ExecuteResultAsync_Throws_IfServicesNotRegistered() { // Arrange var actionContext = new ActionContext(new DefaultHttpContext() { RequestServices = Mock.Of<IServiceProvider>(), }, new RouteData(), new ActionDescriptor()); var expected = "Unable to find the required services. Please add all the required services by calling " + $"'IServiceCollection.AddControllersWithViews()' inside the call to 'ConfigureServices(...)' " + "in the application startup code."; var viewResult = new ViewComponentResult(); // Act var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => viewResult.ExecuteResultAsync(actionContext)); // Assert Assert.Equal(expected, ex.Message); } [Fact] public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult { Arguments = new { name = "World!" }, ViewData = null, TempData = null, ViewComponentName = "Text" }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // No assert, just confirm it didn't throw } [Fact] public async Task ExecuteResultAsync_Throws_IfNameOrTypeIsNotSet() { // Arrange var expected = "Either the 'ViewComponentName' or 'ViewComponentType' " + "property must be set in order to invoke a view component."; var actionContext = CreateActionContext(); var viewComponentResult = new ViewComponentResult { TempData = _tempDataDictionary, }; // Act and Assert var exception = await Assert.ThrowsAsync<InvalidOperationException>( () => viewComponentResult.ExecuteResultAsync(actionContext)); Assert.Equal(expected, exception.Message); } [Fact] public async Task ExecuteResultAsync_Throws_IfViewComponentCouldNotBeFound_ByName() { // Arrange var expected = "A view component named 'Text' could not be found. A view component must be " + "a public non-abstract class, not contain any generic parameters, and either be decorated " + "with 'ViewComponentAttribute' or have a class name ending with the 'ViewComponent' suffix. " + "A view component must not be decorated with 'NonViewComponentAttribute'."; var actionContext = CreateActionContext(); var viewComponentResult = new ViewComponentResult { ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act and Assert var exception = await Assert.ThrowsAsync<InvalidOperationException>( () => viewComponentResult.ExecuteResultAsync(actionContext)); Assert.Equal(expected, exception.Message); } [Fact] public async Task ExecuteResultAsync_Throws_IfViewComponentCouldNotBeFound_ByType() { // Arrange var expected = $"A view component named '{typeof(TextViewComponent).FullName}' could not be found. " + "A view component must be a public non-abstract class, not contain any generic parameters, and either be decorated " + "with 'ViewComponentAttribute' or have a class name ending with the 'ViewComponent' suffix. " + "A view component must not be decorated with 'NonViewComponentAttribute'."; var actionContext = CreateActionContext(); var services = CreateServices(diagnosticListener: null, context: actionContext.HttpContext); services.AddSingleton<IViewComponentSelector>(); var viewComponentResult = new ViewComponentResult { ViewComponentType = typeof(TextViewComponent), TempData = _tempDataDictionary, }; // Act and Assert var exception = await Assert.ThrowsAsync<InvalidOperationException>( () => viewComponentResult.ExecuteResultAsync(actionContext)); Assert.Equal(expected, exception.Message); } [Fact] public async Task ExecuteResultAsync_ExecutesSyncViewComponent() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); } [Fact] public async Task ExecuteResultAsync_UsesDictionaryArguments() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new Dictionary<string, object> { ["name"] = "World!" }, ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); } [Fact] public async Task ExecuteResultAsync_ExecutesAsyncViewComponent() { // Arrange var methodInfo = typeof(AsyncTextViewComponent).GetMethod(nameof(AsyncTextViewComponent.InvokeAsync)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.AsyncText", ShortName = "AsyncText", TypeInfo = typeof(AsyncTextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "AsyncText", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello-Async, World!", body); } [Fact] public async Task ExecuteResultAsync_ExecutesViewComponent_AndWritesDiagnosticListener() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var adapter = new TestDiagnosticListener(); var actionContext = CreateActionContext(adapter, descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); Assert.NotNull(adapter.BeforeViewComponent?.ActionDescriptor); Assert.NotNull(adapter.BeforeViewComponent?.ViewComponentContext); Assert.NotNull(adapter.BeforeViewComponent?.ViewComponent); Assert.NotNull(adapter.AfterViewComponent?.ActionDescriptor); Assert.NotNull(adapter.AfterViewComponent?.ViewComponentContext); Assert.NotNull(adapter.AfterViewComponent?.ViewComponentResult); Assert.NotNull(adapter.AfterViewComponent?.ViewComponent); } [Fact] public async Task ExecuteResultAsync_ExecutesViewComponent_ByShortName() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); } [Fact] public async Task ExecuteResultAsync_ExecutesViewComponent_ByFullName() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Full.Name.Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); } [Fact] public async Task ExecuteResultAsync_ExecutesViewComponent_ByType() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentType = typeof(TextViewComponent), TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal("Hello, World!", body); } [Fact] public async Task ExecuteResultAsync_WithCustomViewComponentHelper() { // Arrange var expected = "Hello from custom helper"; var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var result = Task.FromResult<IHtmlContent>(new HtmlContentBuilder().AppendHtml(expected)); var helper = Mock.Of<IViewComponentHelper>(h => h.InvokeAsync(It.IsAny<Type>(), It.IsAny<object>()) == result); var httpContext = new DefaultHttpContext(); var services = CreateServices(diagnosticListener: null, httpContext, new[] { descriptor }); services.AddSingleton<IViewComponentHelper>(helper); httpContext.RequestServices = services.BuildServiceProvider(); httpContext.Response.Body = new MemoryStream(); var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentType = typeof(TextViewComponent), TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal(expected, body); } [Fact] public async Task ExecuteResultAsync_WithCustomViewComponentHelper_ForLargeText() { // Arrange var expected = new string('a', 64 * 1024 * 1024); var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var result = Task.FromResult<IHtmlContent>(new HtmlContentBuilder().AppendHtml(expected)); var helper = Mock.Of<IViewComponentHelper>(h => h.InvokeAsync(It.IsAny<Type>(), It.IsAny<object>()) == result); var httpContext = new DefaultHttpContext(); var services = CreateServices(diagnosticListener: null, httpContext, new[] { descriptor }); services.AddSingleton<IViewComponentHelper>(helper); httpContext.RequestServices = services.BuildServiceProvider(); httpContext.Response.Body = new MemoryStream(); var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentType = typeof(TextViewComponent), TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var body = ReadBody(actionContext.HttpContext.Response); Assert.Equal(expected, body); } [Fact] public async Task ExecuteResultAsync_SetsStatusCode() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentType = typeof(TextViewComponent), StatusCode = 404, TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert Assert.Equal(404, actionContext.HttpContext.Response.StatusCode); } public static TheoryData<string, string> ViewComponentResultContentTypeData { get { return new TheoryData<string, string> { { null, "text/html; charset=utf-8" }, { "text/foo", "text/foo" }, { "text/foo;p1=p1-value", "text/foo; p1=p1-value" }, { new MediaTypeHeaderValue("text/foo") { Encoding = Encoding.ASCII }.ToString(), "text/foo; charset=us-ascii" } }; } } [Theory] [MemberData(nameof(ViewComponentResultContentTypeData))] public async Task ViewComponentResult_SetsContentTypeHeader( string contentType, string expectedContentType) { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var contentTypeBeforeViewResultExecution = contentType?.ToString(); var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", ContentType = contentType, TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert var resultContentType = actionContext.HttpContext.Response.ContentType; MediaTypeAssert.Equal(expectedContentType, resultContentType); // Check if the original instance provided by the user has not changed. // Since we do not have access to the new instance created within the view executor, // check if at least the content is the same. var contentTypeAfterViewResultExecution = contentType?.ToString(); MediaTypeAssert.Equal(contentTypeBeforeViewResultExecution, contentTypeAfterViewResultExecution); } [Fact] public async Task ViewComponentResult_SetsContentTypeHeader_OverrideResponseContentType() { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); var expectedContentType = "text/html; charset=utf-8"; actionContext.HttpContext.Response.ContentType = "application/x-will-be-overridden"; var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", ContentType = new MediaTypeHeaderValue("text/html") { Encoding = Encoding.UTF8 }.ToString(), TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType); } [Theory] [InlineData("text/foo", "text/foo; charset=utf-8")] [InlineData("text/foo; p1=p1-value", "text/foo; p1=p1-value; charset=utf-8")] [InlineData("text/foo; p1=p1-value; charset=us-ascii", "text/foo; p1=p1-value; charset=us-ascii")] public async Task ViewComponentResult_NoContentTypeSet_PreservesResponseContentType( string responseContentType, string expectedContentType) { // Arrange var methodInfo = typeof(TextViewComponent).GetMethod(nameof(TextViewComponent.Invoke)); var descriptor = new ViewComponentDescriptor() { FullName = "Full.Name.Text", ShortName = "Text", TypeInfo = typeof(TextViewComponent).GetTypeInfo(), MethodInfo = methodInfo, Parameters = methodInfo.GetParameters(), }; var actionContext = CreateActionContext(descriptor); actionContext.HttpContext.Response.ContentType = expectedContentType; var viewComponentResult = new ViewComponentResult() { Arguments = new { name = "World!" }, ViewComponentName = "Text", TempData = _tempDataDictionary, }; // Act await viewComponentResult.ExecuteResultAsync(actionContext); // Assert Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType); } private IServiceCollection CreateServices( object diagnosticListener, HttpContext context, params ViewComponentDescriptor[] descriptors) { var httpContext = new DefaultHttpContext(); var diagnosticSource = new DiagnosticListener("Microsoft.AspNetCore"); if (diagnosticListener != null) { diagnosticSource.SubscribeWithAdapter(diagnosticListener); } var services = new ServiceCollection(); services.AddSingleton<DiagnosticListener>(diagnosticSource); services.AddSingleton<ViewComponentInvokerCache>(); services.AddSingleton(Options.Create(new MvcViewOptions())); services.AddTransient<IViewComponentHelper, DefaultViewComponentHelper>(); services.AddSingleton<IViewComponentSelector, DefaultViewComponentSelector>(); services.AddSingleton<IViewComponentDescriptorCollectionProvider, DefaultViewComponentDescriptorCollectionProvider>(); services.AddSingleton<IViewComponentInvokerFactory, DefaultViewComponentInvokerFactory>(); services.AddSingleton<ITypeActivatorCache, TypeActivatorCache>(); services.AddSingleton<IViewComponentActivator, DefaultViewComponentActivator>(); services.AddSingleton<IViewComponentFactory, DefaultViewComponentFactory>(); services.AddSingleton<IViewComponentDescriptorProvider>(new FixedSetViewComponentDescriptorProvider(descriptors)); services.AddSingleton<IModelMetadataProvider, EmptyModelMetadataProvider>(); services.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance); services.AddSingleton<ITempDataDictionaryFactory, TempDataDictionaryFactory>(); services.AddSingleton<ITempDataProvider, SessionStateTempDataProvider>(); services.AddSingleton<TempDataSerializer, DefaultTempDataSerializer>(); services.AddSingleton<HtmlEncoder, HtmlTestEncoder>(); services.AddSingleton<IViewBufferScope, TestViewBufferScope>(); services.AddSingleton<IActionResultExecutor<ViewComponentResult>, ViewComponentResultExecutor>(); services.AddSingleton<IHttpResponseStreamWriterFactory, TestHttpResponseStreamWriterFactory>(); return services; } private HttpContext CreateHttpContext(object diagnosticListener, params ViewComponentDescriptor[] descriptors) { var httpContext = new DefaultHttpContext(); var services = CreateServices(diagnosticListener, httpContext, descriptors); httpContext.Response.Body = new MemoryStream(); httpContext.RequestServices = services.BuildServiceProvider(); return httpContext; } private ActionContext CreateActionContext(object diagnosticListener, params ViewComponentDescriptor[] descriptors) { return new ActionContext(CreateHttpContext(diagnosticListener, descriptors), new RouteData(), new ActionDescriptor()); } private ActionContext CreateActionContext(params ViewComponentDescriptor[] descriptors) { return CreateActionContext(null, descriptors); } private class FixedSetViewComponentDescriptorProvider : IViewComponentDescriptorProvider { private readonly ViewComponentDescriptor[] _descriptors; public FixedSetViewComponentDescriptorProvider(params ViewComponentDescriptor[] descriptors) { _descriptors = descriptors ?? new ViewComponentDescriptor[0]; } public IEnumerable<ViewComponentDescriptor> GetViewComponents() { return _descriptors; } } private static string ReadBody(HttpResponse response) { response.Body.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(response.Body)) { return reader.ReadToEnd(); } } private class TextViewComponent : ViewComponent { public HtmlString Invoke(string name) { return new HtmlString("Hello, " + name); } } private class AsyncTextViewComponent : ViewComponent { public Task<HtmlString> InvokeAsync(string name) { return Task.FromResult(new HtmlString("Hello-Async, " + name)); } } }