295 lines
12 KiB
C#
295 lines
12 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.IO;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
|
using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.AspNetCore.Testing;
|
|
using Microsoft.Extensions.Options;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|
{
|
|
public class DefaultValidationHtmlAttributeProviderTest
|
|
{
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void AddValidationAttributes_AddsAttributes()
|
|
{
|
|
// Arrange
|
|
var expectedMessage = $"The field {nameof(Model.HasValidatorsProperty)} must be a number.";
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var attributeProvider = GetAttributeProvider(metadataProvider);
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
// Act
|
|
attributeProvider.AddValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Collection(
|
|
attributes,
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val", kvp.Key);
|
|
Assert.Equal("true", kvp.Value);
|
|
},
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val-number", kvp.Key);
|
|
Assert.Equal(expectedMessage, kvp.Value);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
[ReplaceCulture]
|
|
public void AddAndTrackValidationAttributes_AddsAttributes()
|
|
{
|
|
// Arrange
|
|
var expectedMessage = $"The field {nameof(Model.HasValidatorsProperty)} must be a number.";
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var attributeProvider = GetAttributeProvider(metadataProvider);
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
// Act
|
|
attributeProvider.AddAndTrackValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
nameof(Model.HasValidatorsProperty),
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Collection(
|
|
attributes,
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val", kvp.Key);
|
|
Assert.Equal("true", kvp.Value);
|
|
},
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val-number", kvp.Key);
|
|
Assert.Equal(expectedMessage, kvp.Value);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void AddValidationAttributes_AddsNothing_IfClientSideValidationDisabled()
|
|
{
|
|
// Arrange
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var attributeProvider = GetAttributeProvider(metadataProvider);
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
viewContext.ClientValidationEnabled = false;
|
|
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
// Act
|
|
attributeProvider.AddValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Empty(attributes);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddAndTrackValidationAttributes_DoesNotCallAddMethod_IfClientSideValidationDisabled()
|
|
{
|
|
// Arrange
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
viewContext.ClientValidationEnabled = false;
|
|
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
var attributeProviderMock = new Mock<ValidationHtmlAttributeProvider>() { CallBase = true };
|
|
attributeProviderMock
|
|
.Setup(p => p.AddValidationAttributes(
|
|
It.IsAny<ViewContext>(),
|
|
It.IsAny<ModelExplorer>(),
|
|
It.IsAny<IDictionary<string, string>>()))
|
|
.Verifiable();
|
|
var attributeProvider = attributeProviderMock.Object;
|
|
|
|
// Act
|
|
attributeProvider.AddAndTrackValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
nameof(Model.HasValidatorsProperty),
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Empty(attributes);
|
|
attributeProviderMock.Verify(
|
|
p => p.AddValidationAttributes(
|
|
It.IsAny<ViewContext>(),
|
|
It.IsAny<ModelExplorer>(),
|
|
It.IsAny<IDictionary<string, string>>()),
|
|
Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddValidationAttributes_AddsAttributes_EvenIfPropertyAlreadyRendered()
|
|
{
|
|
// Arrange
|
|
var expectedMessage = $"The field {nameof(Model.HasValidatorsProperty)} must be a number.";
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var attributeProvider = GetAttributeProvider(metadataProvider);
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
viewContext.FormContext.RenderedField(nameof(Model.HasValidatorsProperty), value: true);
|
|
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
// Act
|
|
attributeProvider.AddValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Collection(
|
|
attributes,
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val", kvp.Key);
|
|
Assert.Equal("true", kvp.Value);
|
|
},
|
|
kvp =>
|
|
{
|
|
Assert.Equal("data-val-number", kvp.Key);
|
|
Assert.Equal(expectedMessage, kvp.Value);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void AddAndTrackValidationAttributes_DoesNotCallAddMethod_IfPropertyAlreadyRendered()
|
|
{
|
|
// Arrange
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
viewContext.FormContext.RenderedField(nameof(Model.HasValidatorsProperty), value: true);
|
|
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.HasValidatorsProperty));
|
|
|
|
var attributeProviderMock = new Mock<ValidationHtmlAttributeProvider>() { CallBase = true };
|
|
attributeProviderMock
|
|
.Setup(p => p.AddValidationAttributes(
|
|
It.IsAny<ViewContext>(),
|
|
It.IsAny<ModelExplorer>(),
|
|
It.IsAny<IDictionary<string, string>>()))
|
|
.Verifiable();
|
|
var attributeProvider = attributeProviderMock.Object;
|
|
|
|
// Act
|
|
attributeProvider.AddAndTrackValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
nameof(Model.HasValidatorsProperty),
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Empty(attributes);
|
|
attributeProviderMock.Verify(
|
|
p => p.AddValidationAttributes(
|
|
It.IsAny<ViewContext>(),
|
|
It.IsAny<ModelExplorer>(),
|
|
It.IsAny<IDictionary<string, string>>()),
|
|
Times.Never);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddValidationAttributes_AddsNothing_IfPropertyHasNoValidators()
|
|
{
|
|
// Arrange
|
|
var metadataProvider = new EmptyModelMetadataProvider();
|
|
var attributeProvider = GetAttributeProvider(metadataProvider);
|
|
var viewContext = GetViewContext<Model>(model: null, metadataProvider: metadataProvider);
|
|
var attributes = new SortedDictionary<string, string>(StringComparer.Ordinal);
|
|
var modelExplorer = metadataProvider
|
|
.GetModelExplorerForType(typeof(Model), model: null)
|
|
.GetExplorerForProperty(nameof(Model.Property));
|
|
|
|
// Act
|
|
attributeProvider.AddValidationAttributes(
|
|
viewContext,
|
|
modelExplorer,
|
|
attributes);
|
|
|
|
// Assert
|
|
Assert.Empty(attributes);
|
|
}
|
|
|
|
private static ViewContext GetViewContext<TModel>(TModel model, IModelMetadataProvider metadataProvider)
|
|
{
|
|
var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
|
var viewData = new ViewDataDictionary<TModel>(metadataProvider, actionContext.ModelState)
|
|
{
|
|
Model = model,
|
|
};
|
|
|
|
return new ViewContext(
|
|
actionContext,
|
|
Mock.Of<IView>(),
|
|
viewData,
|
|
Mock.Of<ITempDataDictionary>(),
|
|
TextWriter.Null,
|
|
new HtmlHelperOptions());
|
|
}
|
|
|
|
private static ValidationHtmlAttributeProvider GetAttributeProvider(IModelMetadataProvider metadataProvider)
|
|
{
|
|
// Add validation properties for float, double and decimal properties. Ignore everything else.
|
|
var mvcViewOptions = new MvcViewOptions();
|
|
mvcViewOptions.ClientModelValidatorProviders.Add(new NumericClientModelValidatorProvider());
|
|
|
|
var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
|
mvcViewOptionsAccessor.SetupGet(accessor => accessor.Value).Returns(mvcViewOptions);
|
|
|
|
return new DefaultValidationHtmlAttributeProvider(
|
|
mvcViewOptionsAccessor.Object,
|
|
metadataProvider,
|
|
new ClientValidatorCache());
|
|
}
|
|
|
|
private class Model
|
|
{
|
|
public double HasValidatorsProperty { get; set; }
|
|
|
|
public string Property { get; set; }
|
|
}
|
|
}
|
|
}
|