parent
9d5364cf9b
commit
eef8884d0f
|
|
@ -19,18 +19,20 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
/// Determines whether a <see cref="ITagHelper" />'s required attributes are present, non null, non empty, and
|
||||
/// non whitepsace.
|
||||
/// </summary>
|
||||
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
|
||||
/// <param name="tagHelperContext">The <see cref="TagHelperContext"/>.</param>
|
||||
/// <param name="viewContext">The <see cref="ViewContext"/>.</param>
|
||||
/// <param name="requiredAttributes">
|
||||
/// The attributes the <see cref="ITagHelper" /> requires in order to run.
|
||||
/// </param>
|
||||
/// <param name="logger">An optional <see cref="ILogger"/> to log warning details to.</param>
|
||||
/// <returns>A <see cref="bool"/> indicating whether the <see cref="ITagHelper" /> should run.</returns>
|
||||
public static bool AllRequiredAttributesArePresent(
|
||||
[NotNull] TagHelperContext context,
|
||||
[NotNull] TagHelperContext tagHelperContext,
|
||||
[NotNull] ViewContext viewContext,
|
||||
[NotNull] IEnumerable<string> requiredAttributes,
|
||||
ILogger logger)
|
||||
{
|
||||
var attributes = GetPresentMissingAttributes(context, requiredAttributes);
|
||||
var attributes = GetPresentMissingAttributes(tagHelperContext, requiredAttributes);
|
||||
|
||||
if (attributes.Missing.Any())
|
||||
{
|
||||
|
|
@ -38,7 +40,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
{
|
||||
// At least 1 attribute was present indicating the user intended to use the tag helper,
|
||||
// but at least 1 was missing too, so log a warning with the details.
|
||||
logger.WriteWarning(new MissingAttributeLoggerStructure(context.UniqueId, attributes.Missing));
|
||||
logger.WriteWarning(new MissingAttributeLoggerStructure(
|
||||
tagHelperContext.UniqueId,
|
||||
viewContext.View.Path,
|
||||
attributes.Missing));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
public class MissingAttributeLoggerStructure : ILoggerStructure
|
||||
{
|
||||
private readonly string _uniqueId;
|
||||
private readonly string _viewPath;
|
||||
private readonly IEnumerable<KeyValuePair<string, object>> _values;
|
||||
|
||||
// Internal for unit testing
|
||||
|
|
@ -23,14 +24,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
/// Creates a new <see cref="MissingAttributeLoggerStructure"/>.
|
||||
/// </summary>
|
||||
/// <param name="uniqueId">The unique ID of the HTML element this message applies to.</param>
|
||||
/// <param name="viewPath">The path to the view.</param>
|
||||
/// <param name="missingAttributes">The missing required attributes.</param>
|
||||
public MissingAttributeLoggerStructure(string uniqueId, IEnumerable<string> missingAttributes)
|
||||
public MissingAttributeLoggerStructure(string uniqueId, string viewPath, IEnumerable<string> missingAttributes)
|
||||
{
|
||||
_uniqueId = uniqueId;
|
||||
_viewPath = viewPath;
|
||||
MissingAttributes = missingAttributes;
|
||||
_values = new Dictionary<string, object>
|
||||
{
|
||||
["UniqueId"] = _uniqueId,
|
||||
["ViewPath"] = _viewPath,
|
||||
["MissingAttributes"] = MissingAttributes
|
||||
};
|
||||
}
|
||||
|
|
@ -61,8 +65,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
/// <returns>The message.</returns>
|
||||
public string Format()
|
||||
{
|
||||
return string.Format("Tag Helper unique ID: {0}, Missing attributes: {1}",
|
||||
return string.Format("Tag Helper with ID {0} in view '{1}' is missing attributes: {2}",
|
||||
_uniqueId,
|
||||
_viewPath,
|
||||
string.Join(",", MissingAttributes));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,12 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||
/// <param name="tagHelper">The <see cref="ITagHelper"/>.</param>
|
||||
/// <param name="uniqueId">The value of <see cref="TagHelperContext.UniqueId"/>.</param>
|
||||
public void LogDetails<TTagHelper>([NotNull] ILogger logger, [NotNull] TTagHelper tagHelper, string uniqueId)
|
||||
/// <param name="viewPath">The path to the view the <see cref="ITagHelper"/> is on.</param>
|
||||
public void LogDetails<TTagHelper>(
|
||||
[NotNull] ILogger logger,
|
||||
[NotNull] TTagHelper tagHelper,
|
||||
string uniqueId,
|
||||
string viewPath)
|
||||
where TTagHelper : ITagHelper
|
||||
{
|
||||
if (logger.IsEnabled(LogLevel.Warning) && PartiallyMatchedAttributes.Any())
|
||||
|
|
@ -50,7 +55,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
attribute => PartiallyMatchedAttributes.Contains(
|
||||
attribute, StringComparer.OrdinalIgnoreCase)));
|
||||
|
||||
logger.WriteWarning(new PartialModeMatchLoggerStructure<TMode>(uniqueId, partialOnlyMatches));
|
||||
logger.WriteWarning(new PartialModeMatchLoggerStructure<TMode>(uniqueId, viewPath, partialOnlyMatches));
|
||||
}
|
||||
|
||||
if (logger.IsEnabled(LogLevel.Verbose) && !FullMatches.Any())
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
public class PartialModeMatchLoggerStructure<TMode> : ILoggerStructure
|
||||
{
|
||||
private readonly string _uniqueId;
|
||||
private readonly string _viewPath;
|
||||
private readonly IEnumerable<ModeMatchAttributes<TMode>> _partialMatches;
|
||||
private readonly IEnumerable<KeyValuePair<string, object>> _values;
|
||||
|
||||
|
|
@ -24,16 +25,20 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
/// Creates a new <see cref="PartialModeMatchLoggerStructure{TMode}"/>.
|
||||
/// </summary>
|
||||
/// <param name="uniqueId">The unique ID of the HTML element this message applies to.</param>
|
||||
/// <param name="viewPath">The path to the view.</param>
|
||||
/// <param name="partialMatches">The set of modes with partial required attributes.</param>
|
||||
public PartialModeMatchLoggerStructure(
|
||||
string uniqueId,
|
||||
string viewPath,
|
||||
[NotNull] IEnumerable<ModeMatchAttributes<TMode>> partialMatches)
|
||||
{
|
||||
_uniqueId = uniqueId;
|
||||
_viewPath = viewPath;
|
||||
_partialMatches = partialMatches;
|
||||
_values = new Dictionary<string, object>
|
||||
{
|
||||
["UniqueId"] = _uniqueId,
|
||||
["ViewPath"] = _viewPath,
|
||||
["PartialMatches"] = partialMatches
|
||||
};
|
||||
}
|
||||
|
|
@ -65,8 +70,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
public string Format()
|
||||
{
|
||||
var newLine = Environment.NewLine;
|
||||
return
|
||||
string.Format($"Tag Helper {_uniqueId} had partial matches while determining mode:{newLine}\t{{0}}",
|
||||
return string.Format(
|
||||
$"Tag Helper with ID {_uniqueId} in view '{_viewPath}' had partial matches while determining mode:{newLine}\t{{0}}",
|
||||
string.Join($"{newLine}\t", _partialMatches.Select(partial =>
|
||||
string.Format($"Mode '{partial.Mode}' missing attributes:{newLine}\t\t{{0}} ",
|
||||
string.Join($"{newLine}\t\t", partial.MissingAttributes)))));
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
Debug.Assert(modeResult.FullMatches.Select(match => match.Mode).Distinct().Count() <= 1,
|
||||
$"There should only be one mode match, check the {nameof(ModeDetails)}");
|
||||
|
||||
modeResult.LogDetails(Logger, this, context.UniqueId);
|
||||
modeResult.LogDetails(Logger, this, context.UniqueId, ViewContext.View.Path);
|
||||
|
||||
if (!modeResult.FullMatches.Any())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,14 +50,21 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
[Activate]
|
||||
protected internal ILogger<ScriptTagHelper> Logger { get; set; }
|
||||
|
||||
[Activate]
|
||||
protected internal ViewContext ViewContext { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (!AttributeMatcher.AllRequiredAttributesArePresent(context, RequiredAttributes, Logger))
|
||||
if (!AttributeMatcher.AllRequiredAttributesArePresent(context, ViewContext, RequiredAttributes, Logger))
|
||||
{
|
||||
if (Logger.IsEnabled(LogLevel.Verbose))
|
||||
{
|
||||
Logger.WriteVerbose("Skipping processing for {0} {1}", nameof(ScriptTagHelper), context.UniqueId);
|
||||
Logger.WriteVerbose(
|
||||
"Skipping processing for {0} with ID {1} on view '{2}'",
|
||||
nameof(ScriptTagHelper),
|
||||
context.UniqueId,
|
||||
ViewContext.View.Path);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
var logger = MakeLogger(LogLevel.Verbose);
|
||||
var tagHelper = new Mock<ITagHelper>();
|
||||
var uniqueId = "id";
|
||||
var viewPath = "Views/Home/Index.cshtml";
|
||||
|
||||
// Act
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId);
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId, viewPath);
|
||||
|
||||
// Assert
|
||||
Mock.Get(logger).Verify(l => l.Write(
|
||||
|
|
@ -44,9 +45,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
var logger = MakeLogger(LogLevel.Verbose);
|
||||
var tagHelper = new Mock<ITagHelper>();
|
||||
var uniqueId = "id";
|
||||
var viewPath = "Views/Home/Index.cshtml";
|
||||
|
||||
// Act
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId);
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId, viewPath);
|
||||
|
||||
// Assert
|
||||
Mock.Get(logger).Verify(l => l.Write(
|
||||
|
|
@ -74,9 +76,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
var logger = MakeLogger(LogLevel.Verbose);
|
||||
var tagHelper = new Mock<ITagHelper>();
|
||||
var uniqueId = "id";
|
||||
var viewPath = "Views/Home/Index.cshtml";
|
||||
|
||||
// Act
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId);
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId, viewPath);
|
||||
|
||||
// Assert
|
||||
Mock.Get(logger).Verify(l => l.Write(
|
||||
|
|
@ -104,9 +107,10 @@ namespace Microsoft.AspNet.Mvc.TagHelpers.Internal
|
|||
var logger = MakeLogger(LogLevel.Critical);
|
||||
var tagHelper = new Mock<ITagHelper>();
|
||||
var uniqueId = "id";
|
||||
var viewPath = "Views/Home/Index.cshtml";
|
||||
|
||||
// Act
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId);
|
||||
modeMatchResult.LogDetails(logger, tagHelper.Object, uniqueId, viewPath);
|
||||
|
||||
// Assert
|
||||
Mock.Get(logger).Verify(l => l.Write(
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Core;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Microsoft.AspNet.Mvc.TagHelpers.Internal;
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||
|
|
@ -31,7 +37,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
attributes.Add("src", srcValue);
|
||||
}
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
var tagHelperContext = MakeTagHelperContext(attributes);
|
||||
var viewContext = MakeViewContext();
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
|
@ -39,12 +46,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var helper = new ScriptTagHelper()
|
||||
{
|
||||
Logger = logger,
|
||||
ViewContext = viewContext,
|
||||
FallbackSrc = "http://www.example.com/blank.js",
|
||||
FallbackTestExpression = "isavailable()",
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Null(output.TagName);
|
||||
|
|
@ -96,15 +104,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
// Arrange
|
||||
Assert.Single(attributes);
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
var tagHelperContext = MakeTagHelperContext(attributes);
|
||||
var viewContext = MakeViewContext();
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
helper.Logger = logger;
|
||||
helper.ViewContext = viewContext;
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
|
|
@ -115,17 +125,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task DoesNotRunWhenAllRequiredAttributesAreMissing()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext();
|
||||
var tagHelperContext = MakeTagHelperContext();
|
||||
var viewContext = MakeViewContext();
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
ViewContext = viewContext
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
|
|
@ -142,15 +154,17 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
// Arrange
|
||||
Assert.Single(attributes);
|
||||
|
||||
var context = MakeTagHelperContext(attributes);
|
||||
var tagHelperContext = MakeTagHelperContext(attributes);
|
||||
var viewContext = MakeViewContext();
|
||||
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
helper.Logger = logger;
|
||||
helper.ViewContext = viewContext;
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
|
|
@ -175,17 +189,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task LogsWhenAllRequiredAttributesAreMissing()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext();
|
||||
var tagHelperContext = MakeTagHelperContext();
|
||||
var viewContext = MakeViewContext();
|
||||
var output = MakeTagHelperOutput("script");
|
||||
var logger = CreateLogger();
|
||||
|
||||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
ViewContext = viewContext
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("script", output.TagName);
|
||||
|
|
@ -203,7 +219,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
public async Task PreservesOrderOfSourceAttributesWhenRun()
|
||||
{
|
||||
// Arrange
|
||||
var context = MakeTagHelperContext(
|
||||
var tagHelperContext = MakeTagHelperContext(
|
||||
attributes: new Dictionary<string, object>
|
||||
{
|
||||
["data-extra"] = "something",
|
||||
|
|
@ -213,6 +229,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
["asp-fallback-test"] = "isavailable()",
|
||||
});
|
||||
|
||||
var viewContext = MakeViewContext();
|
||||
|
||||
var output = MakeTagHelperOutput("link",
|
||||
attributes: new Dictionary<string, string>
|
||||
{
|
||||
|
|
@ -226,12 +244,13 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
var helper = new ScriptTagHelper
|
||||
{
|
||||
Logger = logger,
|
||||
ViewContext = viewContext,
|
||||
FallbackSrc = "~/blank.js",
|
||||
FallbackTestExpression = "http://www.example.com/blank.js",
|
||||
};
|
||||
|
||||
// Act
|
||||
await helper.ProcessAsync(context, output);
|
||||
await helper.ProcessAsync(tagHelperContext, output);
|
||||
|
||||
// Assert
|
||||
Assert.StartsWith("<script data-extra=\"something\" src=\"/blank.js\" data-more=\"else\"", output.Content);
|
||||
|
|
@ -251,6 +270,16 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
|||
getChildContentAsync: () => Task.FromResult(content));
|
||||
}
|
||||
|
||||
private static ViewContext MakeViewContext()
|
||||
{
|
||||
var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor());
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(metadataProvider);
|
||||
var viewContext = new ViewContext(actionContext, Mock.Of<IView>(), viewData, TextWriter.Null);
|
||||
|
||||
return viewContext;
|
||||
}
|
||||
|
||||
private TagHelperOutput MakeTagHelperOutput(string tagName, IDictionary<string, string> attributes = null)
|
||||
{
|
||||
attributes = attributes ?? new Dictionary<string, string>();
|
||||
|
|
|
|||
Loading…
Reference in New Issue