// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.IO; using System.Linq; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.DataAnnotations; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; using Microsoft.Extensions.WebEncoders.Testing; using Moq; namespace Microsoft.AspNetCore.Mvc.Rendering { public class DefaultTemplatesUtilities { public class ObjectTemplateModel { public ObjectTemplateModel() { ComplexInnerModel = new object(); } public string Property1 { get; set; } [Display(Name = "Prop2")] public string Property2 { get; set; } public object ComplexInnerModel { get; set; } } public class ObjectWithScaffoldColumn { public string Property1 { get; set; } [ScaffoldColumn(false)] public string Property2 { get; set; } [ScaffoldColumn(true)] public string Property3 { get; set; } } public static HtmlHelper GetHtmlHelper() { return GetHtmlHelper(model: null); } public static HtmlHelper> GetHtmlHelperForEnumerable() { return GetHtmlHelper>(model: null); } public static HtmlHelper GetHtmlHelper(IUrlHelper urlHelper) { return GetHtmlHelper( model: null, urlHelper: urlHelper, viewEngine: CreateViewEngine(), provider: TestModelMetadataProvider.CreateDefaultProvider()); } public static HtmlHelper GetHtmlHelper(IHtmlGenerator htmlGenerator) { var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(); return GetHtmlHelper( new ViewDataDictionary(metadataProvider), CreateUrlHelper(), CreateViewEngine(), metadataProvider, localizerFactory: null, innerHelperWrapper: null, htmlGenerator: htmlGenerator, idAttributeDotReplacement: null); } public static HtmlHelper GetHtmlHelper(ViewDataDictionary viewData) { return GetHtmlHelper( viewData, CreateUrlHelper(), CreateViewEngine(), TestModelMetadataProvider.CreateDefaultProvider(), localizerFactory: null, innerHelperWrapper: null, htmlGenerator: null, idAttributeDotReplacement: null); } public static HtmlHelper GetHtmlHelper( ViewDataDictionary viewData, string idAttributeDotReplacement) { return GetHtmlHelper( viewData, CreateUrlHelper(), CreateViewEngine(), TestModelMetadataProvider.CreateDefaultProvider(), localizerFactory: null, innerHelperWrapper: null, htmlGenerator: null, idAttributeDotReplacement: idAttributeDotReplacement); } public static HtmlHelper GetHtmlHelper(TModel model) { return GetHtmlHelper(model, CreateViewEngine()); } public static HtmlHelper GetHtmlHelper(TModel model, string idAttributeDotReplacement) { var provider = TestModelMetadataProvider.CreateDefaultProvider(); var viewData = new ViewDataDictionary(provider); viewData.Model = model; return GetHtmlHelper( viewData, CreateUrlHelper(), CreateViewEngine(), provider, localizerFactory: null, innerHelperWrapper: null, htmlGenerator: null, idAttributeDotReplacement: idAttributeDotReplacement); } public static HtmlHelper> GetHtmlHelperForEnumerable(TModel model) { return GetHtmlHelper>(new TModel[] { model }); } public static HtmlHelper GetHtmlHelper(IModelMetadataProvider provider) { return GetHtmlHelper(model: default(TModel), provider: provider); } public static HtmlHelper GetHtmlHelper(IModelMetadataProvider provider) { return GetHtmlHelper(model: null, provider: provider); } public static HtmlHelper> GetHtmlHelperForEnumerable( IModelMetadataProvider provider) { return GetHtmlHelper>(model: null, provider: provider); } public static HtmlHelper GetHtmlHelper(TModel model, IModelMetadataProvider provider) { return GetHtmlHelper(model, CreateUrlHelper(), CreateViewEngine(), provider); } public static HtmlHelper GetHtmlHelper( TModel model, ICompositeViewEngine viewEngine, IStringLocalizerFactory stringLocalizerFactory = null) { return GetHtmlHelper( model, CreateUrlHelper(), viewEngine, TestModelMetadataProvider.CreateDefaultProvider(stringLocalizerFactory), stringLocalizerFactory); } public static HtmlHelper GetHtmlHelper( TModel model, ICompositeViewEngine viewEngine, Func innerHelperWrapper) { return GetHtmlHelper( model, CreateUrlHelper(), viewEngine, TestModelMetadataProvider.CreateDefaultProvider(), localizerFactory: null, innerHelperWrapper); } public static HtmlHelper GetHtmlHelper( TModel model, IUrlHelper urlHelper, ICompositeViewEngine viewEngine, IModelMetadataProvider provider, IStringLocalizerFactory localizerFactory = null) { return GetHtmlHelper(model, urlHelper, viewEngine, provider, localizerFactory, innerHelperWrapper: null); } public static HtmlHelper GetHtmlHelper( TModel model, IUrlHelper urlHelper, ICompositeViewEngine viewEngine, IModelMetadataProvider provider, IStringLocalizerFactory localizerFactory, Func innerHelperWrapper) { var viewData = new ViewDataDictionary(provider); viewData.Model = model; return GetHtmlHelper( viewData, urlHelper, viewEngine, provider, localizerFactory, innerHelperWrapper, htmlGenerator: null, idAttributeDotReplacement: null); } private static HtmlHelper GetHtmlHelper( ViewDataDictionary viewData, IUrlHelper urlHelper, ICompositeViewEngine viewEngine, IModelMetadataProvider provider, IStringLocalizerFactory localizerFactory, Func innerHelperWrapper, IHtmlGenerator htmlGenerator, string idAttributeDotReplacement) { var httpContext = new DefaultHttpContext(); var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); var options = new MvcViewOptions(); if (!string.IsNullOrEmpty(idAttributeDotReplacement)) { options.HtmlHelperOptions.IdAttributeDotReplacement = idAttributeDotReplacement; } var localizationOptions = new MvcDataAnnotationsLocalizationOptions(); var localizationOptionsAccesor = Options.Create(localizationOptions); options.ClientModelValidatorProviders.Add(new DataAnnotationsClientModelValidatorProvider( new ValidationAttributeAdapterProvider(), localizationOptionsAccesor, localizerFactory)); var urlHelperFactory = new Mock(); urlHelperFactory .Setup(f => f.GetUrlHelper(It.IsAny())) .Returns(urlHelper); var expressionTextCache = new ExpressionTextCache(); if (htmlGenerator == null) { htmlGenerator = HtmlGeneratorUtilities.GetHtmlGenerator(provider, urlHelperFactory.Object, options); } // TemplateRenderer will Contextualize this transient service. var innerHelper = (IHtmlHelper)new HtmlHelper( htmlGenerator, viewEngine, provider, new TestViewBufferScope(), new HtmlTestEncoder(), UrlEncoder.Default); if (innerHelperWrapper != null) { innerHelper = innerHelperWrapper(innerHelper); } var serviceProvider = new ServiceCollection() .AddSingleton(viewEngine) .AddSingleton(urlHelperFactory.Object) .AddSingleton(Mock.Of()) .AddSingleton(innerHelper) .AddSingleton() .BuildServiceProvider(); httpContext.RequestServices = serviceProvider; var htmlHelper = new HtmlHelper( htmlGenerator, viewEngine, provider, new TestViewBufferScope(), new HtmlTestEncoder(), UrlEncoder.Default, expressionTextCache); var viewContext = new ViewContext( actionContext, Mock.Of(), viewData, new TempDataDictionary( httpContext, Mock.Of()), new StringWriter(), options.HtmlHelperOptions); htmlHelper.Contextualize(viewContext); return htmlHelper; } private static ICompositeViewEngine CreateViewEngine() { var view = new Mock(); view .Setup(v => v.RenderAsync(It.IsAny())) .Callback(async (ViewContext v) => { view.ToString(); await v.Writer.WriteAsync(FormatOutput(v.ViewData.ModelExplorer)); }) .Returns(Task.FromResult(0)); var viewEngine = new Mock(MockBehavior.Strict); viewEngine .Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.NotFound("MyView", Enumerable.Empty())) .Verifiable(); viewEngine .Setup(v => v.FindView(It.IsAny(), It.IsAny(), /*isMainPage*/ false)) .Returns(ViewEngineResult.Found("MyView", view.Object)) .Verifiable(); return viewEngine.Object; } public static string FormatOutput(IHtmlHelper helper, object model) { var modelExplorer = helper.MetadataProvider.GetModelExplorerForType(model.GetType(), model); return FormatOutput(modelExplorer); } private static string FormatOutput(ModelExplorer modelExplorer) { var metadata = modelExplorer.Metadata; return string.Format( CultureInfo.InvariantCulture, "Model = {0}, ModelType = {1}, PropertyName = {2}, SimpleDisplayText = {3}", modelExplorer.Model ?? "(null)", metadata.ModelType == null ? "(null)" : metadata.ModelType.FullName, metadata.PropertyName ?? "(null)", modelExplorer.GetSimpleDisplayText() ?? "(null)"); } private static IUrlHelper CreateUrlHelper() { return Mock.Of(); } } }