Auto-select `type="text"` for `DateTimeOffset` values
- #6648 - a different take on #4871 - `DateTime` can also round-trip `DateTimeKind.UTC` with `[DataType("datetimeoffset")]` or `[UIHint("datetimeoffset")]` - since they're now handled differently by default, add more `DateTime` tests - expand tests involving `Html5DateRenderingMode.CurrentCulture` nits: make VS-suggested changes to files updated in this PR
This commit is contained in:
parent
1d1a5203db
commit
7e4a8fe479
|
|
@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "Date", "date" },
|
||||
{ "DateTime", "datetime-local" },
|
||||
{ "DateTime-local", "datetime-local" },
|
||||
{ nameof(DateTimeOffset), "text" },
|
||||
{ "Time", "time" },
|
||||
{ nameof(Byte), "number" },
|
||||
{ nameof(SByte), "number" },
|
||||
|
|
@ -234,8 +235,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{
|
||||
foreach (var hint in GetInputTypeHints(modelExplorer))
|
||||
{
|
||||
string inputType;
|
||||
if (_defaultInputTypes.TryGetValue(hint, out inputType))
|
||||
if (_defaultInputTypes.TryGetValue(hint, out var inputType))
|
||||
{
|
||||
inputTypeHint = hint;
|
||||
return inputType;
|
||||
|
|
@ -252,8 +252,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{
|
||||
if (modelExplorer.Model != null)
|
||||
{
|
||||
bool potentialBool;
|
||||
if (!bool.TryParse(modelExplorer.Model.ToString(), out potentialBool))
|
||||
if (!bool.TryParse(modelExplorer.Model.ToString(), out var potentialBool))
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatInputTagHelper_InvalidStringResult(
|
||||
ForAttributeName,
|
||||
|
|
@ -353,8 +352,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
private TagBuilder GenerateHidden(ModelExplorer modelExplorer)
|
||||
{
|
||||
var value = For.Model;
|
||||
var byteArrayValue = value as byte[];
|
||||
if (byteArrayValue != null)
|
||||
if (value is byte[] byteArrayValue)
|
||||
{
|
||||
value = Convert.ToBase64String(byteArrayValue);
|
||||
}
|
||||
|
|
@ -380,7 +378,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
private string GetFormat(ModelExplorer modelExplorer, string inputTypeHint, string inputType)
|
||||
{
|
||||
string format;
|
||||
string rfc3339Format;
|
||||
if (string.Equals("decimal", inputTypeHint, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals("text", inputType, StringComparison.Ordinal) &&
|
||||
string.IsNullOrEmpty(modelExplorer.Metadata.EditFormatString))
|
||||
|
|
@ -389,14 +386,30 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
// EditFormatString has precedence over this fall-back format.
|
||||
format = "{0:0.00}";
|
||||
}
|
||||
else if (_rfc3339Formats.TryGetValue(inputType, out rfc3339Format) &&
|
||||
ViewContext.Html5DateRenderingMode == Html5DateRenderingMode.Rfc3339 &&
|
||||
else if (ViewContext.Html5DateRenderingMode == Html5DateRenderingMode.Rfc3339 &&
|
||||
!modelExplorer.Metadata.HasNonDefaultEditFormat &&
|
||||
(typeof(DateTime) == modelExplorer.Metadata.UnderlyingOrModelType || typeof(DateTimeOffset) == modelExplorer.Metadata.UnderlyingOrModelType))
|
||||
(typeof(DateTime) == modelExplorer.Metadata.UnderlyingOrModelType ||
|
||||
typeof(DateTimeOffset) == modelExplorer.Metadata.UnderlyingOrModelType))
|
||||
{
|
||||
// Rfc3339 mode _may_ override EditFormatString in a limited number of cases e.g. EditFormatString
|
||||
// must be a default format (i.e. came from a built-in [DataType] attribute).
|
||||
format = rfc3339Format;
|
||||
// Rfc3339 mode _may_ override EditFormatString in a limited number of cases. Happens only when
|
||||
// EditFormatString has a default format i.e. came from a [DataType] attribute.
|
||||
if (string.Equals("text", inputType) &&
|
||||
string.Equals(nameof(DateTimeOffset), inputTypeHint, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Auto-select a format that round-trips Offset and sub-Second values in a DateTimeOffset. Not
|
||||
// done if user chose the "text" type in .cshtml file or with data annotations i.e. when
|
||||
// inputTypeHint==null or "text".
|
||||
format = _rfc3339Formats["datetime"];
|
||||
}
|
||||
else if (_rfc3339Formats.TryGetValue(inputType, out var rfc3339Format))
|
||||
{
|
||||
format = rfc3339Format;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise use default EditFormatString.
|
||||
format = modelExplorer.Metadata.EditFormatString;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -428,7 +441,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
fieldType = modelExplorer.Metadata.UnderlyingOrModelType;
|
||||
}
|
||||
|
||||
foreach (string typeName in TemplateRenderer.GetTypeNames(modelExplorer.Metadata, fieldType))
|
||||
foreach (var typeName in TemplateRenderer.GetTypeNames(modelExplorer.Metadata, fieldType))
|
||||
{
|
||||
yield return typeName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,8 +197,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
{
|
||||
var htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributesObject);
|
||||
|
||||
object htmlClassObject;
|
||||
if (htmlAttributes.TryGetValue("class", out htmlClassObject))
|
||||
if (htmlAttributes.TryGetValue("class", out var htmlClassObject))
|
||||
{
|
||||
var htmlClassName = htmlClassObject + " " + className;
|
||||
htmlAttributes["class"] = htmlClassName;
|
||||
|
|
@ -347,10 +346,10 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
return GenerateTextBox(htmlHelper, inputType: "email");
|
||||
}
|
||||
|
||||
public static IHtmlContent DateTimeInputTemplate(IHtmlHelper htmlHelper)
|
||||
public static IHtmlContent DateTimeOffsetTemplate(IHtmlHelper htmlHelper)
|
||||
{
|
||||
ApplyRfc3339DateFormattingIfNeeded(htmlHelper, "{0:yyyy-MM-ddTHH:mm:ss.fffK}");
|
||||
return GenerateTextBox(htmlHelper, inputType: "datetime");
|
||||
return GenerateTextBox(htmlHelper, inputType: "text");
|
||||
}
|
||||
|
||||
public static IHtmlContent DateTimeLocalInputTemplate(IHtmlHelper htmlHelper)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
{ "Date", DefaultEditorTemplates.DateInputTemplate },
|
||||
{ "DateTime", DefaultEditorTemplates.DateTimeLocalInputTemplate },
|
||||
{ "DateTime-local", DefaultEditorTemplates.DateTimeLocalInputTemplate },
|
||||
{ nameof(DateTimeOffset), DefaultEditorTemplates.DateTimeOffsetTemplate },
|
||||
{ "Time", DefaultEditorTemplates.TimeInputTemplate },
|
||||
{ typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||
{ typeof(sbyte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||
|
|
@ -115,7 +116,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
var defaultActions = GetDefaultActions();
|
||||
var modeViewPath = _readOnly ? DisplayTemplateViewPath : EditorTemplateViewPath;
|
||||
|
||||
foreach (string viewName in GetViewNames())
|
||||
foreach (var viewName in GetViewNames())
|
||||
{
|
||||
var viewEngineResult = _viewEngine.GetView(_viewContext.ExecutingFilePath, viewName, isMainPage: false);
|
||||
if (!viewEngineResult.Success)
|
||||
|
|
@ -141,8 +142,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
}
|
||||
}
|
||||
|
||||
Func<IHtmlHelper, IHtmlContent> defaultAction;
|
||||
if (defaultActions.TryGetValue(viewName, out defaultAction))
|
||||
if (defaultActions.TryGetValue(viewName, out var defaultAction))
|
||||
{
|
||||
return defaultAction(MakeHtmlHelper(_viewContext, _viewData));
|
||||
}
|
||||
|
|
@ -255,8 +255,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
{
|
||||
var newHelper = viewContext.HttpContext.RequestServices.GetRequiredService<IHtmlHelper>();
|
||||
|
||||
var contextable = newHelper as IViewContextAware;
|
||||
if (contextable != null)
|
||||
if (newHelper is IViewContextAware contextable)
|
||||
{
|
||||
var newViewContext = new ViewContext(viewContext, viewContext.View, viewData, viewContext.Writer);
|
||||
contextable.Contextualize(newViewContext);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
<validationMessageElement class=""field-validation-error"">An error occurred.</validationMessageElement>
|
||||
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
|
||||
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""2000-01-02T03:04:05.060"" /> </div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""text"" value=""2000-01-02T03:04:05.060+00:00"" /> </div>
|
||||
|
||||
<div class=""validation-summary-errors""><validationSummaryElement>MySummary</validationSummaryElement>
|
||||
<ul><li>A model error occurred.</li>
|
||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
<validationMessageElement class=""field-validation-error"">An error occurred.</validationMessageElement>
|
||||
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
|
||||
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""2000-01-02T03:04:05.060"" /> </div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""text"" value=""2000-01-02T03:04:05.060+00:00"" /> </div>
|
||||
|
||||
False";
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ False";
|
|||
<ValidationInView class=""field-validation-error"" data-valmsg-for=""Error"" data-valmsg-replace=""true"">An error occurred.</ValidationInView>
|
||||
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
|
||||
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" data-val=""true"" data-val-required=""The MyDate field is required."" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""02/01/2000 03:04:05 +00:00"" /> <ValidationInView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInView></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" data-val=""true"" data-val-required=""The MyDate field is required."" id=""MyDate"" name=""MyDate"" type=""text"" value=""02/01/2000 03:04:05 +00:00"" /> <ValidationInView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInView></div>
|
||||
|
||||
True
|
||||
<div class=""validation-summary-errors""><ValidationSummaryInPartialView>MySummary</ValidationSummaryInPartialView>
|
||||
|
|
@ -68,7 +68,7 @@ True
|
|||
<ValidationInPartialView class=""field-validation-error"" data-valmsg-for=""Error"" data-valmsg-replace=""true"">An error occurred.</ValidationInPartialView>
|
||||
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
|
||||
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""02/01/2000 03:04:05 +00:00"" /> <ValidationInPartialView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInPartialView></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""text"" value=""02/01/2000 03:04:05 +00:00"" /> <ValidationInPartialView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInPartialView></div>
|
||||
|
||||
True";
|
||||
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
|
||||
[Theory]
|
||||
[InlineData("datetime", "datetime")]
|
||||
[InlineData(null, "datetime-local")]
|
||||
[InlineData(null, "text")]
|
||||
[InlineData("hidden", "hidden")]
|
||||
public void Process_GeneratesFormattedOutput(string specifiedType, string expectedType)
|
||||
{
|
||||
|
|
@ -457,6 +457,77 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("datetime", "datetime")]
|
||||
[InlineData(null, "datetime-local")]
|
||||
[InlineData("hidden", "hidden")]
|
||||
public void Process_GeneratesFormattedOutput_ForDateTime(string specifiedType, string expectedType)
|
||||
{
|
||||
// Arrange
|
||||
var expectedAttributes = new TagHelperAttributeList
|
||||
{
|
||||
{ "type", expectedType },
|
||||
{ "id", nameof(Model.DateTime) },
|
||||
{ "name", nameof(Model.DateTime) },
|
||||
{ "valid", "from validation attributes" },
|
||||
{ "value", "datetime: 2011-08-31T05:30:45.0000000Z" },
|
||||
};
|
||||
var expectedTagName = "not-input";
|
||||
var container = new Model
|
||||
{
|
||||
DateTime = new DateTime(2011, 8, 31, hour: 5, minute: 30, second: 45, kind: DateTimeKind.Utc),
|
||||
};
|
||||
|
||||
var allAttributes = new TagHelperAttributeList
|
||||
{
|
||||
{ "type", specifiedType },
|
||||
};
|
||||
var context = new TagHelperContext(
|
||||
tagName: "input",
|
||||
allAttributes: allAttributes,
|
||||
items: new Dictionary<object, object>(),
|
||||
uniqueId: "test");
|
||||
var output = new TagHelperOutput(
|
||||
expectedTagName,
|
||||
new TagHelperAttributeList(),
|
||||
getChildContentAsync: (useCachedResult, encoder) =>
|
||||
{
|
||||
throw new Exception("getChildContentAsync should not be called.");
|
||||
})
|
||||
{
|
||||
TagMode = TagMode.StartTagOnly,
|
||||
};
|
||||
|
||||
var htmlGenerator = new TestableHtmlGenerator(new EmptyModelMetadataProvider())
|
||||
{
|
||||
ValidationAttributes =
|
||||
{
|
||||
{ "valid", "from validation attributes" },
|
||||
}
|
||||
};
|
||||
|
||||
var tagHelper = GetTagHelper(
|
||||
htmlGenerator,
|
||||
container,
|
||||
typeof(Model),
|
||||
model: container.DateTime,
|
||||
propertyName: nameof(Model.DateTime),
|
||||
expressionName: nameof(Model.DateTime));
|
||||
tagHelper.Format = "datetime: {0:o}";
|
||||
tagHelper.InputTypeName = specifiedType;
|
||||
|
||||
// Act
|
||||
tagHelper.Process(context, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedAttributes, output.Attributes);
|
||||
Assert.Empty(output.PreContent.GetContent());
|
||||
Assert.Empty(output.Content.GetContent());
|
||||
Assert.Empty(output.PostContent.GetContent());
|
||||
Assert.Equal(TagMode.StartTagOnly, output.TagMode);
|
||||
Assert.Equal(expectedTagName, output.TagName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProcessAsync_CallsGenerateCheckBox_WithExpectedParameters()
|
||||
{
|
||||
|
|
@ -966,6 +1037,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
{ "datetime", null, "datetime-local" },
|
||||
{ "datetime-local", null, "datetime-local" },
|
||||
{ "DATETIME-local", null, "datetime-local" },
|
||||
{ "datetimeOffset", null, "text" },
|
||||
{ "Decimal", "{0:0.00}", "text" },
|
||||
{ "Double", null, "text" },
|
||||
{ "Int16", null, "number" },
|
||||
|
|
@ -1139,15 +1211,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
[InlineData("Date", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-dd}", "date")]
|
||||
[InlineData("DateTime", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
|
||||
[InlineData("DateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
|
||||
[InlineData("DateTimeOffset", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
|
||||
[InlineData("DateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
|
||||
[InlineData("DateTimeOffset", Html5DateRenderingMode.CurrentCulture, null, "text")]
|
||||
[InlineData("DateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "text")]
|
||||
[InlineData("DateTimeLocal", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
|
||||
[InlineData("DateTimeLocal", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
|
||||
[InlineData("Time", Html5DateRenderingMode.CurrentCulture, "{0:t}", "time")] // Format from [DataType].
|
||||
[InlineData("Time", Html5DateRenderingMode.Rfc3339, "{0:HH:mm:ss.fff}", "time")]
|
||||
[InlineData("NullableDate", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-dd}", "date")]
|
||||
[InlineData("NullableDateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
|
||||
[InlineData("NullableDateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
|
||||
[InlineData("NullableDateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "text")]
|
||||
public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributesForRfc3339(
|
||||
string propertyName,
|
||||
Html5DateRenderingMode dateRenderingMode,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ 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;
|
||||
|
||||
|
|
@ -52,6 +53,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
{ "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'" },
|
||||
{ "Byte", "__TextBox__ class='text-box single-line' type='number'" },
|
||||
|
|
@ -776,7 +779,7 @@ Environment.NewLine;
|
|||
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.006]]\" />";
|
||||
"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(
|
||||
|
|
@ -786,7 +789,7 @@ Environment.NewLine;
|
|||
hour: 3,
|
||||
minute: 4,
|
||||
second: 5,
|
||||
millisecond: 6,
|
||||
millisecond: 60,
|
||||
offset: offset);
|
||||
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
|
|
@ -814,13 +817,19 @@ Environment.NewLine;
|
|||
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
|
||||
}
|
||||
|
||||
// DateTime-local is not special-cased unless using Html5DateRenderingMode.Rfc3339.
|
||||
// Html5DateRenderingMode.Rfc3339 is enabled by default.
|
||||
[Theory]
|
||||
[InlineData(null, null, "2000-01-02T03:04:05.060-05:00", "text")]
|
||||
[InlineData("date", "{0:d}", "2000-01-02", "date")]
|
||||
[InlineData("datetime", null, "2000-01-02T03:04:05.006", "datetime-local")]
|
||||
[InlineData("datetime-local", null, "2000-01-02T03:04:05.006", "datetime-local")]
|
||||
[InlineData("time", "{0:t}", "03:04:05.006", "time")]
|
||||
public void Editor_FindsCorrectDateOrTimeTemplate(string dataTypeName, string editFormatString, string expectedFormat, string expectedType)
|
||||
[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", "{0:o}", "2000-01-02T03:04:05.060-05:00", "text")]
|
||||
[InlineData("time", "{0:t}", "03:04:05.060", "time")]
|
||||
public void Editor_FindsCorrectDateOrTimeTemplate(
|
||||
string dataTypeName,
|
||||
string editFormatString,
|
||||
string expectedFormat,
|
||||
string expectedType)
|
||||
{
|
||||
// Arrange
|
||||
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
|
||||
|
|
@ -830,63 +839,7 @@ Environment.NewLine;
|
|||
expectedType +
|
||||
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";
|
||||
|
||||
var offset = TimeSpan.FromHours(0);
|
||||
var model = new DateTimeOffset(
|
||||
year: 2000,
|
||||
month: 1,
|
||||
day: 2,
|
||||
hour: 3,
|
||||
minute: 4,
|
||||
second: 5,
|
||||
millisecond: 6,
|
||||
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.
|
||||
});
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("date", "{0:d}", "2000-01-02", "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("time", "{0:t}", "03:04:05.060", "time")]
|
||||
public void Editor_AppliesRfc3339(string dataTypeName, string editFormatString, string expectedFormat, 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[[" + expectedFormat + "]]\" />";
|
||||
|
||||
// Place DateTime-local value in current timezone.
|
||||
var offset = string.Equals(string.Empty, dataTypeName) ? DateTimeOffset.Now.Offset : TimeSpan.FromHours(0);
|
||||
var offset = TimeSpan.FromHours(-5);
|
||||
var model = new DateTimeOffset(
|
||||
year: 2000,
|
||||
month: 1,
|
||||
|
|
@ -916,7 +869,193 @@ Environment.NewLine;
|
|||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider);
|
||||
helper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339;
|
||||
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", "{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", "{0:o}", "2000-01-02T03:04:05.0600000-05:00", "text")]
|
||||
[InlineData("time", "{0:t}", "03:04", "time")]
|
||||
[ReplaceCulture]
|
||||
public void Editor_FindsCorrectDateOrTimeTemplate_NotRfc3339(
|
||||
string dataTypeName,
|
||||
string editFormatString,
|
||||
string expectedFormat,
|
||||
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[[" + expectedFormat + "]]\" />";
|
||||
|
||||
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.
|
||||
});
|
||||
|
||||
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", "{0:d}", "2000-01-02", "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", "{0:o}", "2000-01-02T03:04:05.060Z", "text")]
|
||||
[InlineData("time", "{0:t}", "03:04:05.060", "time")]
|
||||
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTime(
|
||||
string dataTypeName,
|
||||
string editFormatString,
|
||||
string expectedFormat,
|
||||
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[[" + expectedFormat + "]]\" />";
|
||||
|
||||
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.
|
||||
});
|
||||
|
||||
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", "{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", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
|
||||
[InlineData("time", "{0:t}", "03:04", "time")]
|
||||
[ReplaceCulture]
|
||||
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTimeNotRfc3339(
|
||||
string dataTypeName,
|
||||
string editFormatString,
|
||||
string expectedFormat,
|
||||
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[[" + expectedFormat + "]]\" />";
|
||||
|
||||
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.
|
||||
});
|
||||
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
|
||||
model,
|
||||
Mock.Of<IUrlHelper>(),
|
||||
viewEngine.Object,
|
||||
provider);
|
||||
helper.Html5DateRenderingMode = Html5DateRenderingMode.CurrentCulture;
|
||||
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";
|
||||
|
||||
// Act
|
||||
|
|
@ -927,6 +1066,8 @@ Environment.NewLine;
|
|||
}
|
||||
|
||||
[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")]
|
||||
|
|
|
|||
Loading…
Reference in New Issue