1639 lines
75 KiB
C#
1639 lines
75 KiB
C#
// 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;
|
|
using System.Text.Encodings.Web;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Html;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
|
using Microsoft.AspNetCore.Mvc.TestCommon;
|
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|
{
|
|
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-local'" },
|
|
{ "datetime", "__TextBox__ class='text-box single-line' type='datetime-local'" },
|
|
{ "DateTime-local", "__TextBox__ class='text-box single-line' type='datetime-local'" },
|
|
{ "DATETIME-LOCAL", "__TextBox__ class='text-box single-line' type='datetime-local'" },
|
|
{ "datetimeoffset", "__TextBox__ class='text-box single-line' type='text'" },
|
|
{ "DateTimeOffset", "__TextBox__ class='text-box single-line' type='text'" },
|
|
{ "Time", "__TextBox__ class='text-box single-line' type='time'" },
|
|
{ "time", "__TextBox__ class='text-box single-line' type='time'" },
|
|
{ "Month", "__TextBox__ class='text-box single-line' type='month'" },
|
|
{ "month", "__TextBox__ class='text-box single-line' type='month'" },
|
|
{ "Week", "__TextBox__ class='text-box single-line' type='week'" },
|
|
{ "week", "__TextBox__ class='text-box single-line' type='week'" },
|
|
{ "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'" },
|
|
{ "SINGLE", "__TextBox__ class='text-box single-line'" },
|
|
{ "Double", "__TextBox__ class='text-box single-line'" },
|
|
{ "double", "__TextBox__ class='text-box single-line'" },
|
|
{ "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'" },
|
|
};
|
|
}
|
|
}
|
|
|
|
// label's IHtmlContent -> expected label text
|
|
public static TheoryData<IHtmlContent, string> ObjectTemplate_ChecksWriteTo_NotToStringData
|
|
{
|
|
get
|
|
{
|
|
// Similar to HtmlString.Empty today.
|
|
var noopContentWithEmptyToString = new Mock<IHtmlContent>(MockBehavior.Strict);
|
|
noopContentWithEmptyToString
|
|
.Setup(c => c.ToString())
|
|
.Returns(string.Empty);
|
|
noopContentWithEmptyToString.Setup(c => c.WriteTo(It.IsAny<TextWriter>(), It.IsAny<HtmlEncoder>()));
|
|
|
|
// Similar to an empty StringHtmlContent today.
|
|
var noopContentWithNonEmptyToString = new Mock<IHtmlContent>(MockBehavior.Strict);
|
|
noopContentWithNonEmptyToString
|
|
.Setup(c => c.ToString())
|
|
.Returns(typeof(StringHtmlContent).FullName);
|
|
noopContentWithNonEmptyToString.Setup(c => c.WriteTo(It.IsAny<TextWriter>(), It.IsAny<HtmlEncoder>()));
|
|
|
|
// Makes noop calls on the TextWriter.
|
|
var busyNoopContentWithNonEmptyToString = new Mock<IHtmlContent>(MockBehavior.Strict);
|
|
busyNoopContentWithNonEmptyToString
|
|
.Setup(c => c.ToString())
|
|
.Returns(typeof(StringHtmlContent).FullName);
|
|
busyNoopContentWithNonEmptyToString
|
|
.Setup(c => c.WriteTo(It.IsAny<TextWriter>(), It.IsAny<HtmlEncoder>()))
|
|
.Callback<TextWriter, HtmlEncoder>((writer, encoder) =>
|
|
{
|
|
writer.Write(string.Empty);
|
|
writer.Write(new char[0]);
|
|
writer.Write((char[])null);
|
|
writer.Write((object)null);
|
|
writer.Write((string)null);
|
|
writer.Write(format: "{0}", arg0: null);
|
|
writer.Write(new char[] { 'a', 'b', 'c' }, index: 1, count: 0);
|
|
});
|
|
|
|
// Unrealistic but covers all the bases.
|
|
var writingContentWithEmptyToString = new Mock<IHtmlContent>(MockBehavior.Strict);
|
|
writingContentWithEmptyToString
|
|
.Setup(c => c.ToString())
|
|
.Returns(string.Empty);
|
|
writingContentWithEmptyToString
|
|
.Setup(c => c.WriteTo(It.IsAny<TextWriter>(), It.IsAny<HtmlEncoder>()))
|
|
.Callback<TextWriter, HtmlEncoder>((writer, encoder) => writer.Write("Some string"));
|
|
|
|
// Similar to TagBuilder today.
|
|
var writingContentWithNonEmptyToString = new Mock<IHtmlContent>(MockBehavior.Strict);
|
|
writingContentWithNonEmptyToString
|
|
.Setup(c => c.ToString())
|
|
.Returns(typeof(TagBuilder).FullName);
|
|
writingContentWithNonEmptyToString
|
|
.Setup(c => c.WriteTo(It.IsAny<TextWriter>(), It.IsAny<HtmlEncoder>()))
|
|
.Callback<TextWriter, HtmlEncoder>((writer, encoder) => writer.Write("Some string"));
|
|
|
|
// label's IHtmlContent -> expected label text
|
|
return new TheoryData<IHtmlContent, string>
|
|
{
|
|
// Types HtmlHelper actually uses.
|
|
{ HtmlString.Empty, string.Empty },
|
|
{
|
|
new TagBuilder("label"),
|
|
"<div class=\"HtmlEncode[[editor-label]]\"><label></label></div>" + Environment.NewLine
|
|
},
|
|
|
|
// Another IHtmlContent implementation that does not override ToString().
|
|
{ new StringHtmlContent(string.Empty), string.Empty },
|
|
|
|
// Mocks
|
|
{ noopContentWithEmptyToString.Object, string.Empty },
|
|
{ noopContentWithNonEmptyToString.Object, string.Empty },
|
|
{ busyNoopContentWithNonEmptyToString.Object, string.Empty },
|
|
{
|
|
writingContentWithEmptyToString.Object,
|
|
"<div class=\"HtmlEncode[[editor-label]]\">Some string</div>" + Environment.NewLine
|
|
},
|
|
{
|
|
writingContentWithNonEmptyToString.Object,
|
|
"<div class=\"HtmlEncode[[editor-label]]\">Some string</div>" + Environment.NewLine
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ObjectTemplateEditsSimplePropertiesOnObjectByDefault()
|
|
{
|
|
// Arrange
|
|
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[[Prop2]]</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 model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.ObjectTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
// Prior to aspnet/Mvc#6638 fix, helper did not generate Property1 <label> or containing <div> with this setup.
|
|
// Expect almost the same HTML as in ObjectTemplateEditsSimplePropertiesOnObjectByDefault(). Only difference is
|
|
// the <div class="editor-label">...</div> is not present for Property1.
|
|
[Fact]
|
|
public void ObjectTemplateSkipsLabel_IfDisplayNameIsEmpty()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
"<div class=\"HtmlEncode[[editor-label]]\"><label for=\"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[[Prop2]]</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>(
|
|
nameof(DefaultTemplatesUtilities.ObjectTemplateModel.Property1))
|
|
.DisplayDetails(dd => dd.DisplayName = () => string.Empty);
|
|
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "p1", Property2 = null };
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider);
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.ObjectTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(ObjectTemplate_ChecksWriteTo_NotToStringData))]
|
|
public void ObjectTemplate_ChecksWriteTo_NotToString(IHtmlContent labelContent, string expectedLabel)
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
expectedLabel +
|
|
"<div class=\"HtmlEncode[[editor-field]]\">Model = (null), ModelType = System.String, PropertyName = Property1, SimpleDisplayText = (null) " +
|
|
"</div>" +
|
|
Environment.NewLine +
|
|
expectedLabel +
|
|
"<div class=\"HtmlEncode[[editor-field]]\">Model = (null), ModelType = System.String, PropertyName = Property2, SimpleDisplayText = (null) " +
|
|
"</div>" +
|
|
Environment.NewLine;
|
|
|
|
var helperToCopy = DefaultTemplatesUtilities.GetHtmlHelper();
|
|
var helperMock = new Mock<IHtmlHelper>(MockBehavior.Strict);
|
|
helperMock.SetupGet(h => h.ViewContext).Returns(helperToCopy.ViewContext);
|
|
helperMock.SetupGet(h => h.ViewData).Returns(helperToCopy.ViewData);
|
|
helperMock
|
|
.Setup(h => h.Label(
|
|
It.Is<string>(s => string.Equals("Property1", s, StringComparison.Ordinal) ||
|
|
string.Equals("Property2", s, StringComparison.Ordinal)),
|
|
null, // labelText
|
|
null)) // htmlAttributes
|
|
.Returns(labelContent);
|
|
helperMock
|
|
.Setup(h => h.ValidationMessage(
|
|
It.Is<string>(s => string.Equals("Property1", s, StringComparison.Ordinal) ||
|
|
string.Equals("Property2", s, StringComparison.Ordinal)),
|
|
null, // message
|
|
null, // htmlAttributes
|
|
null)) // tag
|
|
.Returns(HtmlString.Empty);
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.ObjectTemplate(helperMock.Object);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(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, HtmlContentUtilities.HtmlContentToString(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, HtmlContentUtilities.HtmlContentToString(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>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
|
|
var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.ObjectTemplate(htmlHelper);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(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[[Prop2]]</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, HtmlContentUtilities.HtmlContentToString(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
|
|
var result = DefaultEditorTemplates.ObjectTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void HiddenInputTemplate_ReturnsValueAndHiddenInput()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
"HtmlEncode[[Formatted string]]<input id=\"HtmlEncode[[FieldPrefix]]\" name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[Model string]]\" />";
|
|
|
|
var model = "Model string";
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
|
var templateInfo = html.ViewData.TemplateInfo;
|
|
templateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used below.
|
|
templateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.HiddenInputTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void HiddenInputTemplate_HonoursHideSurroundingHtml()
|
|
{
|
|
// Arrange
|
|
var expected = "<input id=\"HtmlEncode[[FieldPrefix]]\" name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[hidden]]\" value=\"HtmlEncode[[Model string]]\" />";
|
|
|
|
var model = "Model string";
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<string>().DisplayDetails(dd =>
|
|
{
|
|
dd.HideSurroundingHtml = true;
|
|
});
|
|
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(model, provider: provider);
|
|
|
|
var templateInfo = html.ViewData.TemplateInfo;
|
|
templateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
templateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.HiddenInputTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void MultilineTextTemplate_ReturnsTextArea()
|
|
{
|
|
// Arrange
|
|
var expected =
|
|
"<textarea class=\"HtmlEncode[[text-box multi-line]]\" id=\"HtmlEncode[[FieldPrefix]]\" name=\"HtmlEncode[[FieldPrefix]]\">" +
|
|
Environment.NewLine +
|
|
"HtmlEncode[[Formatted string]]</textarea>";
|
|
|
|
var model = "Model string";
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
|
var templateInfo = html.ViewData.TemplateInfo;
|
|
templateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used below.
|
|
templateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.MultilineTemplate(html);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void PasswordTemplate_ReturnsInputElement_IgnoresValues()
|
|
{
|
|
// Arrange
|
|
var expected = "<input class=\"HtmlEncode[[text-box single-line password]]\" " +
|
|
"id=\"HtmlEncode[[FieldPrefix]]\" name=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"type=\"HtmlEncode[[password]]\" />";
|
|
|
|
// Template ignores Model.
|
|
var model = "Model string";
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model);
|
|
var viewData = helper.ViewData;
|
|
var templateInfo = viewData.TemplateInfo;
|
|
templateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Template ignores FormattedModelValue, ModelState and ViewData.
|
|
templateInfo.FormattedModelValue = "Formatted string";
|
|
viewData.ModelState.SetModelValue("FieldPrefix", "Raw model string", "Attempted model string");
|
|
viewData["FieldPrefix"] = "ViewData string";
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.PasswordTemplate(helper);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void PasswordTemplate_ReturnsInputElement_UsesHtmlAttributes()
|
|
{
|
|
// Arrange
|
|
var expected = "<input class=\"HtmlEncode[[super text-box single-line password]]\" " +
|
|
"id=\"HtmlEncode[[FieldPrefix]]\" name=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"type=\"HtmlEncode[[password]]\" value=\"HtmlEncode[[Html attributes string]]\" />";
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper<string>(model: null);
|
|
var viewData = helper.ViewData;
|
|
var templateInfo = viewData.TemplateInfo;
|
|
templateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
viewData["htmlAttributes"] = new { @class = "super", value = "Html attributes string" };
|
|
|
|
// Act
|
|
var result = DefaultEditorTemplates.PasswordTemplate(helper);
|
|
|
|
// Assert
|
|
Assert.Equal(expected, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void Editor_CallsExpectedHtmlHelper(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
viewEngine.Object,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
helper.ViewData["Property1"] = "True";
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.Editor(
|
|
"Property1",
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void EditorFor_CallsExpectedHtmlHelper(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
viewEngine.Object,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.EditorFor(
|
|
anotherModel => anotherModel.Property1,
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void Editor_CallsExpectedHtmlHelper_DataTypeName(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = templateName;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
helper.ViewData["Property1"] = "True";
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.Editor(
|
|
"Property1",
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void EditorFor_CallsExpectedHtmlHelper_DataTypeName(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = templateName;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.EditorFor(
|
|
anotherModel => anotherModel.Property1,
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void Editor_CallsExpectedHtmlHelper_TemplateHint(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
|
|
{
|
|
dd.TemplateHint = templateName;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
helper.ViewData["Property1"] = "True";
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.Editor(
|
|
"Property1",
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[MemberData(nameof(TemplateNameData))]
|
|
public void EditorFor_CallsExpectedHtmlHelper_TemplateHint(string templateName, string expectedResult)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "True" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForProperty<DefaultTemplatesUtilities.ObjectTemplateModel>("Property1").DisplayDetails(dd =>
|
|
{
|
|
dd.TemplateHint = templateName;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider,
|
|
innerHelper => new StubbyHtmlHelper(innerHelper));
|
|
|
|
// TemplateBuilder sets FormattedModelValue before calling TemplateRenderer and it's used in most templates.
|
|
helper.ViewData.TemplateInfo.FormattedModelValue = "Formatted string";
|
|
|
|
// Act
|
|
var result = helper.EditorFor(
|
|
anotherModel => anotherModel.Property1,
|
|
templateName,
|
|
htmlFieldName: null,
|
|
additionalViewData: null);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void Editor_FindsViewDataMember()
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
helper.ViewData["Property1"] = "ViewData string";
|
|
|
|
// Act
|
|
var result = helper.Editor("Property1");
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" id=\"HtmlEncode[[Property1]]\" name=\"HtmlEncode[[Property1]]\" type=\"HtmlEncode[[text]]\" value=\"HtmlEncode[[ViewData string]]\" />",
|
|
HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void Editor_InputTypeDateTime_RendersAsDateTime()
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
|
|
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[datetime]]\" value=\"HtmlEncode[[2000-01-02T03:04:05.060+00:00]]\" />";
|
|
|
|
var offset = TimeSpan.FromHours(0);
|
|
var model = new DateTimeOffset(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
offset: offset);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(
|
|
string.Empty,
|
|
new { htmlAttributes = new { type = "datetime" } });
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
// Html5DateRenderingMode.Rfc3339 is enabled by default.
|
|
[Theory]
|
|
[InlineData(null, null, "2000-01-02T03:04:05.060-05:00", "text")]
|
|
[InlineData("date", null, "2000-01-02", "date")]
|
|
[InlineData("date", "{0:d}", "02/01/2000", "date")]
|
|
[InlineData("datetime", null, "2000-01-02T03:04:05.060", "datetime-local")]
|
|
[InlineData("datetime-local", null, "2000-01-02T03:04:05.060", "datetime-local")]
|
|
[InlineData("DateTimeOffset", null, "2000-01-02T03:04:05.060-05:00", "text")]
|
|
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000-05:00", "text")]
|
|
[InlineData("time", null, "03:04:05.060", "time")]
|
|
[InlineData("time", "{0:t}", "03:04", "time")]
|
|
[InlineData("month", null, "2000-01", "month")]
|
|
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
|
|
[InlineData("week", null, "1999-W52", "week")]
|
|
[InlineData("week", "{0:yyyy-'W1'}", "2000-W1", "week")]
|
|
[ReplaceCulture]
|
|
public void Editor_FindsCorrectDateOrTimeTemplate_WithTimeOffset(
|
|
string dataTypeName,
|
|
string editFormatString,
|
|
string expectedValue,
|
|
string expectedType)
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
|
|
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
|
|
expectedType +
|
|
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
|
|
|
|
var offset = TimeSpan.FromHours(-5);
|
|
var model = new DateTimeOffset(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
offset: offset);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = dataTypeName;
|
|
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
|
|
dd.HasNonDefaultEditFormat = true;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
// Html5DateRenderingMode.Rfc3339 can be disabled.
|
|
[Theory]
|
|
[InlineData(null, null, "02/01/2000 03:04:05 -05:00", "text")]
|
|
[InlineData("date", null, "02/01/2000 03:04:05 -05:00", "date")]
|
|
[InlineData("date", "{0:d}", "02/01/2000", "date")]
|
|
[InlineData("datetime", null, "02/01/2000 03:04:05 -05:00", "datetime-local")]
|
|
[InlineData("datetime-local", null, "02/01/2000 03:04:05 -05:00", "datetime-local")]
|
|
[InlineData("DateTimeOffset", null, "02/01/2000 03:04:05 -05:00", "text")]
|
|
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000-05:00", "text")]
|
|
[InlineData("time", null, "02/01/2000 03:04:05 -05:00", "time")]
|
|
[InlineData("time", "{0:t}", "03:04", "time")]
|
|
[InlineData("month", null, "2000-01", "month")]
|
|
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
|
|
[InlineData("week", null, "1999-W52", "week")]
|
|
[InlineData("week", "{0:yyyy-'W1'}", "2000-W1", "week")]
|
|
[ReplaceCulture]
|
|
public void Editor_FindsCorrectDateOrTimeTemplate_WithTimeOffset_NotRfc3339(
|
|
string dataTypeName,
|
|
string editFormatString,
|
|
string expectedValue,
|
|
string expectedType)
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
|
|
var expectedInput =
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
|
|
expectedType +
|
|
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
|
|
|
|
var offset = TimeSpan.FromHours(-5);
|
|
var model = new DateTimeOffset(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
offset: offset);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = dataTypeName;
|
|
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
|
|
dd.HasNonDefaultEditFormat = true;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
helper.Html5DateRenderingMode = Html5DateRenderingMode.CurrentCulture;
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
// Html5DateRenderingMode.Rfc3339 is enabled by default.
|
|
[Theory]
|
|
[InlineData(null, null, "2000-01-02T03:04:05.060", "datetime-local")]
|
|
[InlineData("date", null, "2000-01-02", "date")]
|
|
[InlineData("date", "{0:d}", "02/01/2000", "date")]
|
|
[InlineData("datetime", null, "2000-01-02T03:04:05.060", "datetime-local")]
|
|
[InlineData("datetime-local", null, "2000-01-02T03:04:05.060", "datetime-local")]
|
|
[InlineData("DateTimeOffset", null, "2000-01-02T03:04:05.060Z", "text")]
|
|
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
|
|
[InlineData("time", null, "03:04:05.060", "time")]
|
|
[InlineData("time", "{0:t}", "03:04", "time")]
|
|
[InlineData("month", null, "2000-01", "month")]
|
|
[InlineData("month", "{0:yyyy/MM}", "2000/01", "month")]
|
|
[InlineData("week", null, "1999-W52", "week")]
|
|
[InlineData("Week", "{0:yyyy/'W1'}", "2000/W1", "week")]
|
|
[ReplaceCulture]
|
|
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTime(
|
|
string dataTypeName,
|
|
string editFormatString,
|
|
string expectedValue,
|
|
string expectedType)
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTime");
|
|
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
|
|
expectedType +
|
|
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
|
|
|
|
var model = new DateTime(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
kind: DateTimeKind.Utc);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<DateTime>().DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = dataTypeName;
|
|
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
|
|
dd.HasNonDefaultEditFormat = true;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
// Html5DateRenderingMode.Rfc3339 can be disabled.
|
|
[Theory]
|
|
[InlineData(null, null, "02/01/2000 03:04:05", "datetime-local")]
|
|
[InlineData("date", null, "02/01/2000 03:04:05", "date")]
|
|
[InlineData("date", "{0:d}", "02/01/2000", "date")]
|
|
[InlineData("datetime", null, "02/01/2000 03:04:05", "datetime-local")]
|
|
[InlineData("datetime-local", null, "02/01/2000 03:04:05", "datetime-local")]
|
|
[InlineData("DateTimeOffset", null, "02/01/2000 03:04:05", "text")]
|
|
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
|
|
[InlineData("time", "{0:t}", "03:04", "time")]
|
|
[InlineData("month", null, "2000-01", "month")]
|
|
[InlineData("month", "{0:yyyy/MM}", "2000/01", "month")]
|
|
[InlineData("week", null, "1999-W52", "week")]
|
|
[InlineData("Week", "{0:yyyy/'W1'}", "2000/W1", "week")]
|
|
[ReplaceCulture]
|
|
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTimeNotRfc3339(
|
|
string dataTypeName,
|
|
string editFormatString,
|
|
string expectedValue,
|
|
string expectedType)
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTime");
|
|
var expectedInput =
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
|
|
expectedType +
|
|
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
|
|
|
|
var model = new DateTime(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
kind: DateTimeKind.Utc);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<DateTime>().DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = dataTypeName;
|
|
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
|
|
dd.HasNonDefaultEditFormat = true;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
helper.Html5DateRenderingMode = Html5DateRenderingMode.CurrentCulture;
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null, Html5DateRenderingMode.CurrentCulture, "text")]
|
|
[InlineData(null, Html5DateRenderingMode.Rfc3339, "text")]
|
|
[InlineData("date", Html5DateRenderingMode.CurrentCulture, "date")]
|
|
[InlineData("date", Html5DateRenderingMode.Rfc3339, "date")]
|
|
[InlineData("datetime", Html5DateRenderingMode.CurrentCulture, "datetime-local")]
|
|
[InlineData("datetime", Html5DateRenderingMode.Rfc3339, "datetime-local")]
|
|
[InlineData("datetime-local", Html5DateRenderingMode.CurrentCulture, "datetime-local")]
|
|
[InlineData("datetime-local", Html5DateRenderingMode.Rfc3339, "datetime-local")]
|
|
[InlineData("time", Html5DateRenderingMode.CurrentCulture, "time")]
|
|
[InlineData("time", Html5DateRenderingMode.Rfc3339, "time")]
|
|
[InlineData("month", Html5DateRenderingMode.CurrentCulture, "month")]
|
|
[InlineData("month", Html5DateRenderingMode.Rfc3339, "month")]
|
|
[InlineData("week", Html5DateRenderingMode.CurrentCulture, "week")]
|
|
[InlineData("week", Html5DateRenderingMode.Rfc3339, "week")]
|
|
public void Editor_AppliesNonDefaultEditFormat(string dataTypeName, Html5DateRenderingMode renderingMode, string expectedType)
|
|
{
|
|
// Arrange
|
|
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
|
|
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
|
|
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
|
|
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
|
|
expectedType +
|
|
"]]\" value=\"HtmlEncode[[Formatted as 2000-01-02T03:04:05.0600000+00:00]]\" />";
|
|
|
|
var offset = TimeSpan.FromHours(0);
|
|
var model = new DateTimeOffset(
|
|
year: 2000,
|
|
month: 1,
|
|
day: 2,
|
|
hour: 3,
|
|
minute: 4,
|
|
second: 5,
|
|
millisecond: 60,
|
|
offset: offset);
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
|
|
var provider = new TestModelMetadataProvider();
|
|
provider.ForType<DateTimeOffset>().DisplayDetails(dd =>
|
|
{
|
|
dd.DataTypeName = dataTypeName;
|
|
dd.EditFormatString = "Formatted as {0:O}"; // What [DataType] does for given type.
|
|
dd.HasNonDefaultEditFormat = true;
|
|
});
|
|
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
|
model,
|
|
Mock.Of<IUrlHelper>(),
|
|
viewEngine.Object,
|
|
provider);
|
|
|
|
helper.Html5DateRenderingMode = renderingMode; // Ignored due to HasNonDefaultEditFormat.
|
|
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
|
|
|
// Act
|
|
var result = helper.Editor(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void EditorFor_FindsModel()
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
helper.ViewData["Property1"] = "ViewData string";
|
|
|
|
// Act
|
|
var result = helper.EditorFor(m => m.Property1);
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" id=\"HtmlEncode[[Property1]]\" name=\"HtmlEncode[[Property1]]\" type=\"HtmlEncode[[text]]\" value=\"HtmlEncode[[Model string]]\" />",
|
|
HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void Editor_FindsModel_IfNoViewDataMember()
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Model string" };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
|
|
// Act
|
|
var result = helper.Editor("Property1");
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" id=\"HtmlEncode[[Property1]]\" name=\"HtmlEncode[[Property1]]\" type=\"HtmlEncode[[text]]\" value=\"HtmlEncode[[Model string]]\" />",
|
|
HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
public void EditorFor_FindsModel_EvenIfNullOrEmpty(string propertyValue)
|
|
{
|
|
// Arrange
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = propertyValue, };
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
helper.ViewData["Property1"] = "ViewData string";
|
|
|
|
// Act
|
|
var result = helper.EditorFor(m => m.Property1);
|
|
|
|
// Assert
|
|
Assert.Equal(
|
|
"<input class=\"HtmlEncode[[text-box single-line]]\" id=\"HtmlEncode[[Property1]]\" name=\"HtmlEncode[[Property1]]\" type=\"HtmlEncode[[text]]\" value=\"\" />",
|
|
HtmlContentUtilities.HtmlContentToString(result));
|
|
}
|
|
|
|
[Fact]
|
|
public void EditorFor_DoesNotWrapExceptionThrowsDuringViewRendering()
|
|
{
|
|
// Arrange
|
|
var expectedMessage = "my exception message";
|
|
var model = new DefaultTemplatesUtilities.ObjectTemplateModel { Property1 = "Test string", };
|
|
var view = new Mock<IView>();
|
|
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
|
.Returns(Task.Run(() =>
|
|
{
|
|
throw new FormatException(expectedMessage);
|
|
}));
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.Found("test-view", view.Object));
|
|
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
|
helper.ViewData["Property1"] = "ViewData string";
|
|
|
|
// Act and Assert
|
|
var ex = Assert.Throws<FormatException>(() => helper.EditorFor(m => m.Property1));
|
|
Assert.Equal(expectedMessage, ex.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void EditorForModel_CallsFindView_WithExpectedPath()
|
|
{
|
|
// Arrange
|
|
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
|
viewEngine
|
|
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
|
viewEngine
|
|
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "EditorTemplates/String", /*isMainPage*/ false))
|
|
.Returns(ViewEngineResult.Found(string.Empty, new Mock<IView>().Object))
|
|
.Verifiable();
|
|
var html = DefaultTemplatesUtilities.GetHtmlHelper(new object(), viewEngine: viewEngine.Object);
|
|
|
|
// Act & Assert
|
|
html.Editor(expression: string.Empty, templateName: null, htmlFieldName: null, additionalViewData: null);
|
|
viewEngine.Verify();
|
|
}
|
|
|
|
private class OrderedModel
|
|
{
|
|
[Display(Order = 10001)]
|
|
public string LastProperty { get; set; }
|
|
|
|
public string Property3 { get; set; }
|
|
public string Property1 { get; set; }
|
|
public string Property2 { get; set; }
|
|
|
|
[Display(Order = 23)]
|
|
public string OrderedProperty3 { get; set; }
|
|
[Display(Order = 23)]
|
|
public string OrderedProperty2 { get; set; }
|
|
[Display(Order = 23)]
|
|
public string OrderedProperty1 { get; set; }
|
|
}
|
|
|
|
private class StubbyHtmlHelper : IHtmlHelper, IViewContextAware
|
|
{
|
|
private readonly IHtmlHelper _innerHelper;
|
|
|
|
public StubbyHtmlHelper(IHtmlHelper innerHelper)
|
|
{
|
|
_innerHelper = innerHelper;
|
|
}
|
|
|
|
public Html5DateRenderingMode Html5DateRenderingMode
|
|
{
|
|
get { return _innerHelper.Html5DateRenderingMode; }
|
|
set { _innerHelper.Html5DateRenderingMode = value; }
|
|
}
|
|
|
|
public string IdAttributeDotReplacement
|
|
{
|
|
get { return _innerHelper.IdAttributeDotReplacement; }
|
|
}
|
|
|
|
public IModelMetadataProvider MetadataProvider
|
|
{
|
|
get { return _innerHelper.MetadataProvider; }
|
|
}
|
|
|
|
public dynamic ViewBag
|
|
{
|
|
get { return _innerHelper.ViewBag; }
|
|
}
|
|
|
|
public ViewContext ViewContext
|
|
{
|
|
get { return _innerHelper.ViewContext; }
|
|
}
|
|
|
|
public ViewDataDictionary ViewData
|
|
{
|
|
get { return _innerHelper.ViewData; }
|
|
}
|
|
|
|
public ITempDataDictionary TempData
|
|
{
|
|
get { return _innerHelper.TempData; }
|
|
}
|
|
|
|
public UrlEncoder UrlEncoder
|
|
{
|
|
get { return _innerHelper.UrlEncoder; }
|
|
}
|
|
|
|
public void Contextualize(ViewContext viewContext)
|
|
{
|
|
(_innerHelper as IViewContextAware)?.Contextualize(viewContext);
|
|
}
|
|
|
|
public IHtmlContent ActionLink(
|
|
string linkText,
|
|
string actionName,
|
|
string controllerName,
|
|
string protocol,
|
|
string hostname,
|
|
string fragment,
|
|
object routeValues,
|
|
object htmlAttributes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent AntiForgeryToken()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public MvcForm BeginForm(
|
|
string actionName,
|
|
string controllerName,
|
|
object routeValues,
|
|
FormMethod method,
|
|
bool? antiforgery,
|
|
object htmlAttributes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public MvcForm BeginRouteForm(
|
|
string routeName,
|
|
object routeValues,
|
|
FormMethod method,
|
|
bool? antiforgery,
|
|
object htmlAttributes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent CheckBox(string name, bool? isChecked, object htmlAttributes)
|
|
{
|
|
return HelperName("__CheckBox__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent Display(
|
|
string expression,
|
|
string templateName,
|
|
string htmlFieldName,
|
|
object additionalViewData)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string DisplayName(string expression)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string DisplayText(string name)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent DropDownList(
|
|
string name,
|
|
IEnumerable<SelectListItem> selectList,
|
|
string optionLabel,
|
|
object htmlAttributes)
|
|
{
|
|
return HelperName("__DropDownList__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent Editor(
|
|
string expression,
|
|
string templateName,
|
|
string htmlFieldName,
|
|
object additionalViewData)
|
|
{
|
|
return _innerHelper.Editor(expression, templateName, htmlFieldName, additionalViewData);
|
|
}
|
|
|
|
public string Encode(string value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string Encode(object value)
|
|
{
|
|
return _innerHelper.Encode(value);
|
|
}
|
|
|
|
public void EndForm()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string FormatValue(object value, string format)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string GenerateIdFromName(string name)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IEnumerable<SelectListItem> GetEnumSelectList(Type enumType)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent Hidden(string name, object value, object htmlAttributes)
|
|
{
|
|
return HelperName("__Hidden__", htmlAttributes);
|
|
}
|
|
|
|
public string Id(string name)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent Label(string expression, string labelText, object htmlAttributes)
|
|
{
|
|
return HelperName("__Label__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent ListBox(string name, IEnumerable<SelectListItem> selectList, object htmlAttributes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string Name(string name)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public Task<IHtmlContent> PartialAsync(
|
|
string partialViewName,
|
|
object model,
|
|
ViewDataDictionary viewData)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent Password(string name, object value, object htmlAttributes)
|
|
{
|
|
return HelperName("__Password__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent RadioButton(string name, object value, bool? isChecked, object htmlAttributes)
|
|
{
|
|
return HelperName("__RadioButton__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent Raw(object value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent Raw(string value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public Task RenderPartialAsync(string partialViewName, object model, ViewDataDictionary viewData)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent RouteLink(
|
|
string linkText,
|
|
string routeName,
|
|
string protocol,
|
|
string hostName,
|
|
string fragment,
|
|
object routeValues,
|
|
object htmlAttributes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public IHtmlContent TextArea(string name, string value, int rows, int columns, object htmlAttributes)
|
|
{
|
|
return HelperName("__TextArea__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent TextBox(string name, object value, string format, object htmlAttributes)
|
|
{
|
|
return HelperName("__TextBox__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent ValidationMessage(string modelName, string message, object htmlAttributes, string tag)
|
|
{
|
|
return HelperName("__ValidationMessage__", htmlAttributes);
|
|
}
|
|
|
|
public IHtmlContent ValidationSummary(
|
|
bool excludePropertyErrors,
|
|
string message,
|
|
object htmlAttributes,
|
|
string tag)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public string Value(string name, string format)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private IHtmlContent HelperName(string name, object htmlAttributes)
|
|
{
|
|
var htmlAttributesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
|
|
var htmlAttributesString =
|
|
string.Join(" ", htmlAttributesDictionary.Select(entry => $"{ entry.Key }='{ entry.Value }'"));
|
|
var helperName = $"{ name } { htmlAttributesString }";
|
|
|
|
return new HtmlString(helperName.TrimEnd());
|
|
}
|
|
}
|
|
}
|
|
}
|