context: // 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.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using Microsoft.AspNet.Http;
  using Microsoft.AspNet.Mvc.ModelBinding;
  using Microsoft.AspNet.Mvc.ModelBinding.Validation;
  using Microsoft.AspNet.Mvc.Rendering;
  using Microsoft.AspNet.Mvc.Rendering.Internal;
  using Microsoft.AspNet.Testing;
  using Microsoft.Framework.Internal;
  using Microsoft.Framework.WebEncoders;
  using Moq;
  using Xunit;
  
  namespace Microsoft.AspNet.Mvc.Core
  {
      public class DefaultEditorTemplatesTest
      {
          // Mappings from templateName to expected result when using StubbyHtmlHelper.
          public static TheoryData<string, string> TemplateNameData
          {
              get
              {
                  return new TheoryData<string, string>
                  {
                      { null, "__TextBox__ class='text-box single-line'" },
                      { string.Empty, "__TextBox__ class='text-box single-line'" },
                      { "EmailAddress", "__TextBox__ class='text-box single-line' type='email'" },
                      { "emailaddress", "__TextBox__ class='text-box single-line' type='email'" },
                      { "HiddenInput", "HtmlEncode[[True]]__Hidden__" }, // Hidden also generates value by default.
                      { "HIDDENINPUT", "HtmlEncode[[True]]__Hidden__" },
                      { "MultilineText", "__TextArea__ class='text-box multi-line'" },
                      { "multilinetext", "__TextArea__ class='text-box multi-line'" },
                      { "Password", "__Password__ class='text-box single-line password'" },
                      { "PASSWORD", "__Password__ class='text-box single-line password'" },
                      { "PhoneNumber", "__TextBox__ class='text-box single-line' type='tel'" },
                      { "phonenumber", "__TextBox__ class='text-box single-line' type='tel'" },
                      { "Text", "__TextBox__ class='text-box single-line'" },
                      { "TEXT", "__TextBox__ class='text-box single-line'" },
                      { "Url", "__TextBox__ class='text-box single-line' type='url'" },
                      { "url", "__TextBox__ class='text-box single-line' type='url'" },
                      { "Date", "__TextBox__ class='text-box single-line' type='date'" },
                      { "DATE", "__TextBox__ class='text-box single-line' type='date'" },
                      { "DateTime", "__TextBox__ class='text-box single-line' type='datetime'" },
                      { "datetime", "__TextBox__ class='text-box single-line' type='datetime'" },
                      { "DateTime-local", "__TextBox__ class='text-box single-line' type='datetime-local'" },
                      { "DATETIME-LOCAL", "__TextBox__ class='text-box single-line' type='datetime-local'" },
                      { "Time", "__TextBox__ class='text-box single-line' type='time'" },
                      { "time", "__TextBox__ class='text-box single-line' type='time'" },
                      { "Byte", "__TextBox__ class='text-box single-line' type='number'" },
                      { "BYTE", "__TextBox__ class='text-box single-line' type='number'" },
                      { "SByte", "__TextBox__ class='text-box single-line' type='number'" },
                      { "sbyte", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Int16", "__TextBox__ class='text-box single-line' type='number'" },
                      { "INT16", "__TextBox__ class='text-box single-line' type='number'" },
                      { "UInt16", "__TextBox__ class='text-box single-line' type='number'" },
                      { "uint16", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Int32", "__TextBox__ class='text-box single-line' type='number'" },
                      { "INT32", "__TextBox__ class='text-box single-line' type='number'" },
                      { "UInt32", "__TextBox__ class='text-box single-line' type='number'" },
                      { "uint32", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Int64", "__TextBox__ class='text-box single-line' type='number'" },
                      { "INT64", "__TextBox__ class='text-box single-line' type='number'" },
                      { "UInt64", "__TextBox__ class='text-box single-line' type='number'" },
                      { "uint64", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Single", "__TextBox__ class='text-box single-line' type='number'" },
                      { "SINGLE", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Double", "__TextBox__ class='text-box single-line' type='number'" },
                      { "double", "__TextBox__ class='text-box single-line' type='number'" },
                      { "Boolean", "__CheckBox__ class='check-box'" }, // Not tri-state b/c string is not a Nullable type.
                      { "BOOLEAN", "__CheckBox__ class='check-box'" },
                      { "Decimal", "__TextBox__ class='text-box single-line'" },
                      { "decimal", "__TextBox__ class='text-box single-line'" },
                      { "String", "__TextBox__ class='text-box single-line'" },
                      { "STRING", "__TextBox__ class='text-box single-line'" },
                      { typeof(IFormFile).Name, "__TextBox__ class='text-box single-line' type='file'" },
                      { TemplateRenderer.IEnumerableOfIFormFileName,
                          "__TextBox__ class='text-box single-line' type='file' multiple='multiple'" },
                  };
              }
          }
  
          [Fact]
          public void ObjectTemplateEditsSimplePropertiesOnObjectByDefault()
          {
              var expected =
                  "<div class=\"HtmlEncode[[editor-label]]\"><label for=\"HtmlEncode[[Property1]]\">HtmlEncode[[Property1]]</label></div>" + Environment.NewLine
                + "<div class=\"HtmlEncode[[editor-field]]\">Model = p1, ModelType = System.String, PropertyName = Property1," +
                      " SimpleDisplayText = p1 " +
                      "<span class=\"HtmlEncode[[field-validation-valid]]\" data-valmsg-for=\"HtmlEncode[[Property1]]\" data-valmsg-replace=\"HtmlEncode[[true]]\">" +
                      "</span></div>" + Environment.NewLine
                + "<div class=\"HtmlEncode[[editor-label]]\"><label for=\"HtmlEncode[[Property2]]\">HtmlEncode[[Property2]]</label></div>" + Environment.NewLine
                + "<div class=\"HtmlEncode[[editor-field]]\">Model = (null), ModelType = System.String, PropertyName = Property2," +
                      " SimpleDisplayText = (null) " +
                      "<span class=\"HtmlEncode[[field-validation-valid]]\" data-valmsg-for=\"HtmlEncode[[Property2]]\" data-valmsg-replace=\"HtmlEncode[[true]]\">" +
                      "</span></div>" + Environment.NewLine;
  
              // Arrange
              var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
              var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
  
              // Act
              var result = DefaultEditorTemplates.ObjectTemplate(html);
  
              // Assert
              Assert.Equal(expected, result);
          }
  
          [Fact]
          public void ObjectTemplateDisplaysNullDisplayTextWithNullModelAndTemplateDepthGreaterThanOne()
          {
              // Arrange
              var provider = new TestModelMetadataProvider();
              provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
              {
                  dd.NullDisplayText = "Null Display Text";
                  dd.SimpleDisplayProperty = "Property1";
              });
  
              var html = DefaultTemplatesUtilities.GetHtmlHelper(provider: provider);
  
              html.ViewData.TemplateInfo.AddVisited("foo");
              html.ViewData.TemplateInfo.AddVisited("bar");
  
              // Act
              var result = DefaultEditorTemplates.ObjectTemplate(html);
  
              // Assert
              Assert.Equal(html.ViewData.ModelMetadata.NullDisplayText, result);
          }
  
          [Theory]
          [MemberData(nameof(DefaultDisplayTemplatesTest.HtmlEncodeData), MemberType = typeof(DefaultDisplayTemplatesTest))]
          public void ObjectTemplateDisplaysSimpleDisplayTextWithNonNullModelTemplateDepthGreaterThanOne(
              string simpleDisplayText,
              bool htmlEncode,
              string expectedResult)
          {
              // Arrange
              var model = new DefaultTemplatesUtilities.ObjectTemplateModel()
              {
                  Property1 = simpleDisplayText,
              };
  
              var provider = new TestModelMetadataProvider();
              provider.ForType<DefaultTemplatesUtilities.ObjectTemplateModel>().DisplayDetails(dd =>
              {
                  dd.HtmlEncode = htmlEncode;
                  dd.NullDisplayText = "Null Display Text";
                  dd.SimpleDisplayProperty = "Property1";
              });
  
              var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
  
              html.ViewData.TemplateInfo.AddVisited("foo");
              html.ViewData.TemplateInfo.AddVisited("bar");
  
              // Act
              var result = DefaultEditorTemplates.ObjectTemplate(html);
  
              // Assert
              Assert.Equal(expectedResult, result);
          }
  
          [Fact]
          public void ObjectTemplate_IgnoresPropertiesWith_ScaffoldColumnFalse()
          {
              // Arrange
              var expected =
  @"<div class=""HtmlEncode[[editor-label]]""><label for=""HtmlEncode[[Property1]]"">HtmlEncode[[Property1]]</label></div>" +
  Environment.NewLine +
  @"<div class=""HtmlEncode[[editor-field]]""><input class=""HtmlEncode[[text-box single-line]]"" id=""HtmlEncode[[Property1]]"" name=""HtmlEncode[[Property1]]"" type=""HtmlEncode[[text]]"" value="""" /> " +
  @"<span class=""HtmlEncode[[field-validation-valid]]"" data-valmsg-for=""HtmlEncode[[Property1]]"" data-valmsg-replace=""HtmlEncode[[true]]""></span></div>" +
  Environment.NewLine +
  @"<div class=""HtmlEncode[[editor-label]]""><label for=""HtmlEncode[[Property3]]"">HtmlEncode[[Property3]]</label></div>" +
  Environment.NewLine +
  @"<div class=""HtmlEncode[[editor-field]]""><input class=""HtmlEncode[[text-box single-line]]"" id=""HtmlEncode[[Property3]]"" name=""HtmlEncode[[Property3]]"" type=""HtmlEncode[[text]]"" value="""" /> " +
  @"<span class=""HtmlEncode[[field-validation-valid]]"" data-valmsg-for=""HtmlEncode[[Property3]]"" data-valmsg-replace=""HtmlEncode[[true]]""></span></div>" +
  Environment.NewLine;
  
              var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
              var viewEngine = new Mock<ICompositeViewEngine>();
              viewEngine.Setup(v => v.FindPartialView(It.IsAny<ActionContext>(), It.IsAny<string>()))
                        .Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
              var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
  
              // Act
              var result = DefaultEditorTemplates.ObjectTemplate(htmlHelper);
  
              // Assert
              Assert.Equal(expected, result);
          }
  
          [Fact]
          public void ObjectTemplate_HonoursHideSurroundingHtml()
          {
              // Arrange
              var expected =
                  "Model = p1, ModelType = System.String, PropertyName = Property1, SimpleDisplayText = p1" +
                  "<div class=\"HtmlEncode[[editor-label]]\"><label for=\"HtmlEncode[[Property2]]\">HtmlEncode[[Property2]]</label></div>" +
                  Environment.NewLine +
                  "<div class=\"HtmlEncode[[editor-field]]\">" +
                      "Model = (null), ModelType = System.String, PropertyName = Property2, SimpleDisplayText = (null) " +
                      "<span class=\"HtmlEncode[[field-validation-valid]]\" data-valmsg-for=\"HtmlEncode[[Property2]]\" data-valmsg-replace=\"HtmlEncode[[true]]\">" +
                      "</span></div>" +
                  Environment.NewLine;
  
              var provider = new TestModelMetadataProvider();
              provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
              {
                  dd.HideSurroundingHtml = true;
              });
  
              var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
              var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
  
              // Act
              var result = DefaultEditorTemplates.ObjectTemplate(html);
  
              // Assert
              Assert.Equal(expected, result);
          }
  
          [Fact]
          public void ObjectTemplate_OrdersProperties_AsExpected()
          {
              // Arrange
              var model = new OrderedModel();
              var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
              var expectedProperties = new List<string>
              {
                  "OrderedProperty3",
                  "OrderedProperty2",
                  "OrderedProperty1",
                  "Property3",
                  "Property1",
                  "Property2",
                  "LastProperty",
              };
  
              var stringBuilder = new StringBuilder();
              foreach (var property in expectedProperties)
              {
                  var label = string.Format(
                      CultureInfo.InvariantCulture,
                      "<div class=\"HtmlEncode[[editor-label]]\"><label for=\"HtmlEncode[[{0}]]\">HtmlEncode[[{0}]]</label></div>",
                      property);
                  stringBuilder.AppendLine(label);
  
                  var value = string.Format(
                      CultureInfo.InvariantCulture,
                      "<div class=\"HtmlEncode[[editor-field]]\">Model = (null), ModelType = System.String, PropertyName = {0}, " +
                      "SimpleDisplayText = (null) " +
                      "<span class=\"HtmlEncode[[field-validation-valid]]\" data-valmsg-for=\"HtmlEncode[[{0}]]\" data-valmsg-replace=\"HtmlEncode[[true]]\">" +
                      "</span></div>",
                      property);
                  stringBuilder.AppendLine(value);
              }
              var expected = stringBuilder.ToString();
  
              // Act
  
gt:             var result = DefaultEditorTemplates.ObjectTemplate(html);